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
| Method | Endpoint | Description |
|---|---|---|
| POST | /shorten | Create 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?
urlsstores the mapping between short keys and original URLssync.RWMutexensures 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?
- The request method is validated
- The URL is extracted from the form data
- A short key is generated
- The mapping is stored safely using a mutex
- 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
# Start the servergo run main.go
# Create a short URLcurl -X POST -d "url=https://example.com" http://localhost:8090/shortenOpening 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.