Author Avatar
Anchal Rawat
Overview

Building a Simple URL Shortener in Go

January 17, 2026
3 min read

I built this project to better understand how web servers work in Go while keeping things clean, simple, and practical. A URL shortener is a great learning project because it combines HTTP handling, hashing, and concurrency in one place.

In this post, I’ll walk through how my Go-based URL shortener works and explain the key ideas behind it.


What This Project Does

At a high level, my URL shortener:

  • Accepts a long URL
  • Generates a short, unique key
  • Stores the mapping in memory
  • Redirects users to the original URL

Available Endpoints

MethodEndpointDescription
POST/shortenCreate a shortened URL
GET/{shortKey}Redirect to original URL

I intentionally used only Go’s standard library to keep the project lightweight and beginner-friendly.


Core Data Structure

At the heart of the application is a simple struct:

type URLShortener struct {
urls map[string]string
mu sync.RWMutex
}

Why this design?

  • urls stores the mapping between short keys and original URLs
  • sync.RWMutex ensures thread-safe access when multiple users hit the server at the same time

Note: Since this data is stored in memory, everything resets when the server restarts. This is acceptable for a learning project.


Generating Short URLs

To generate short keys, I used SHA-256 hashing combined with Base64 encoding:

func generateShortKey(originalURL string) string {
algorithm := sha256.New()
algorithm.Write([]byte(originalURL))
hashBytes := algorithm.Sum(nil)
encoded := base64.URLEncoding.EncodeToString(hashBytes)
return encoded[:8]
}

Why this approach works

  • SHA-256 ensures the same URL always generates the same hash
  • Base64 encoding keeps the output URL-safe
  • Trimming to 8 characters keeps links short and readable

This approach isn’t ideal for large-scale systems due to potential collisions, but it works well for a demo project.


Creating a Short URL

The /shorten endpoint handles URL creation:

func makeShortURL(w http.ResponseWriter, req *http.Request) {
if req.Method != http.MethodPost {
http.Error(w, "Invalid request method", http.StatusMethodNotAllowed)
return
}
originalURL := req.FormValue("url")
if originalURL == "" {
http.Error(w, "URL parameter is missing", http.StatusBadRequest)
return
}
shortKey := generateShortKey(originalURL)
shortener.mu.Lock()
shortener.urls[shortKey] = originalURL
shortener.mu.Unlock()
constructURL := fmt.Sprintf("http://localhost:8090/%s", shortKey)
fmt.Fprintf(w, "Shortened URL: %s\n", constructURL)
}

What happens here?

  1. The request method is validated
  2. The URL is extracted from the form data
  3. A short key is generated
  4. The mapping is stored safely using a mutex
  5. The shortened URL is returned to the client

Redirecting Users

When someone visits a short URL, the root handler takes over:

func handleRedirect(w http.ResponseWriter, req *http.Request) {
shortKey := strings.TrimPrefix(req.URL.Path, "/")
shortener.mu.RLock()
originalURL, ok := shortener.urls[shortKey]
shortener.mu.RUnlock()
if !ok {
http.Error(w, "Short URL not found", http.StatusNotFound)
return
}
http.Redirect(w, req, originalURL, http.StatusFound)
}

If the short key exists, the server responds with a 302 Found redirect, sending the user to the original URL.


Running the Server

Everything is wired together in main:

func main() {
http.HandleFunc("/shorten", makeShortURL)
http.HandleFunc("/", handleRedirect)
fmt.Println("Server starting on port :8090...")
http.ListenAndServe(":8090", nil)
}

Once started, the server listens on port 8090.


How To Test It

Terminal window
# Start the server
go run main.go
# Create a short URL
curl -X POST -d "url=https://example.com" http://localhost:8090/shorten

Opening the returned short URL in a browser redirects to the original site.


Possible Improvements

If I continue developing this project, I would consider:

  • Persisting URLs using a database
  • Handling hash collisions more robustly
  • Adding expiration times for shortened URLs
  • Validating URL formats
  • Returning JSON instead of plain text responses

Final Thoughts

This project helped me better understand how Go handles HTTP servers, concurrency, and cryptographic hashing using only the standard library.

For anyone learning Go, projects like this are an excellent way to build practical experience and confidence.