Practical examples of simple HTTP server examples in Go
A minimal example of a simple HTTP server in Go
Let’s start with the smallest example of a server that is still realistic. This is the classic “hello” server, but written the way Go developers actually write it today:
package main
import (
"fmt"
"log"
"net/http"
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Hello from Go HTTP server")
})
addr := ":8080"
log.Printf("Starting server on %s", addr)
if err := http.ListenAndServe(addr, mux); err != nil {
log.Fatalf("server failed: %v", err)
}
}
``
This is one of the simplest **examples of simple HTTP server examples in Go** you’ll ever see, but it already teaches a few patterns:
- Use `http.NewServeMux` instead of the global `http.DefaultServeMux` for clearer routing.
- Always log startup and shutdown issues.
- Bind to an explicit port (here `:8080`), which is still a common default in 2024.
You can run this with:
```bash
go run main.go
Then open http://localhost:8080 in your browser.
Examples of simple HTTP server examples in Go with multiple routes
Real examples of simple HTTP server examples in Go almost always involve more than one route. Here’s a minimal multi-route setup using the standard library multiplexer:
package main
import (
"fmt"
"log"
"net/http"
)
func homeHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Welcome to the home page")
}
func aboutHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "About: built with Go in 2025")
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", homeHandler)
mux.HandleFunc("/about", aboutHandler)
addr := ":8080"
log.Printf("Listening on %s", addr)
if err := http.ListenAndServe(addr, mux); err != nil {
log.Fatalf("server error: %v", err)
}
}
This pattern scales surprisingly far. Many production Go services still use http.ServeMux directly, especially when they want to avoid extra dependencies or keep a small attack surface.
JSON API: one of the best examples for front-end integration
Most modern backends expose JSON. So one of the best examples of simple HTTP server examples in Go is a tiny JSON API. Here’s a small server that returns a JSON payload for /status:
package main
import (
"encoding/json"
"log"
"net/http"
"time"
)
type StatusResponse struct {
Service string `json:"service"`
Status string `json:"status"`
Time time.Time `json:"time"`
}
func statusHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
resp := StatusResponse{
Service: "example-api",
Status: "ok",
Time: time.Now().UTC(),
}
if err := json.NewEncoder(w).Encode(resp); err != nil {
http.Error(w, "failed to encode JSON", http.StatusInternalServerError)
return
}
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/status", statusHandler)
addr := ":8080"
log.Printf("JSON API listening on %s", addr)
if err := http.ListenAndServe(addr, mux); err != nil {
log.Fatalf("server failed: %v", err)
}
}
This is one of the most practical examples of simple HTTP server examples in Go for front-end developers who just need a JSON endpoint to prototype against.
For a deeper dive into HTTP concepts (methods, status codes, headers), the MDN Web Docs remain a solid reference: https://developer.mozilla.org/en-US/docs/Web/HTTP
Serving static files: real examples you’ll use in small projects
A lot of real examples of simple HTTP server examples in Go involve serving static assets: HTML, CSS, JS, or images built by a front-end toolchain.
Here’s a tiny server that serves files from a ./public directory:
package main
import (
"log"
"net/http"
)
func main() {
fileServer := http.FileServer(http.Dir("./public"))
mux := http.NewServeMux()
mux.Handle("/", fileServer)
addr := ":8080"
log.Printf("Serving static files on %s from ./public", addr)
if err := http.ListenAndServe(addr, mux); err != nil {
log.Fatalf("server error: %v", err)
}
}
Drop an index.html into ./public, and your Go server becomes a tiny static site host. This pattern still shows up in internal tools, admin dashboards, and quick prototypes.
Adding simple logging middleware around handlers
In 2024–2025, observability is not optional. Even for simple servers, you want to know who is hitting your endpoints and how long requests take. A common example of extending simple HTTP server examples in Go is to wrap handlers with logging.
package main
import (
"log"
"net/http"
"time"
)
// loggingMiddleware wraps an http.Handler and logs requests.
func loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
next.ServeHTTP(w, r)
duration := time.Since(start)
log.Printf("%s %s %s", r.Method, r.URL.Path, duration)
})
}
func helloHandler(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("hello with logging"))
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", helloHandler)
loggedMux := loggingMiddleware(mux)
addr := ":8080"
log.Printf("Server with logging on %s", addr)
if err := http.ListenAndServe(addr, loggedMux); err != nil {
log.Fatalf("server failed: %v", err)
}
}
This isn’t a full-blown middleware framework, but it demonstrates the pattern clearly. Many of the best examples from production codebases follow this simple wrapping approach, even when they later swap in structured logging libraries.
Graceful shutdown: production-flavored simple HTTP server
Cloud platforms and container orchestrators (Docker, Kubernetes, ECS) send signals before stopping your process. A realistic example of simple HTTP server examples in Go for 2024–2025 includes graceful shutdown using http.Server and context.
package main
import (
"context"
"log"
"net/http"
"os"
"os/signal"
"syscall"
"time"
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("graceful server"))
})
srv := &http.Server{
Addr: ":8080",
Handler: mux,
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
IdleTimeout: 60 * time.Second,
}
go func() {
log.Printf("Server starting on %s", srv.Addr)
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatalf("listen: %v", err)
}
}()
stop := make(chan os.Signal, 1)
signal.Notify(stop, syscall.SIGINT, syscall.SIGTERM)
<-stop
log.Println("Shutting down...")
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
if err := srv.Shutdown(ctx); err != nil {
log.Printf("graceful shutdown failed: %v", err)
} else {
log.Println("Server stopped cleanly")
}
}
Among all the examples of simple HTTP server examples in Go, this one is the closest to how production services behave. It respects timeouts, listens for signals, and shuts down politely.
For background on why graceful shutdown matters in distributed systems, the ACM has accessible material on reliability and fault tolerance: https://queue.acm.org/
Health checks and readiness: small but real examples
If you plan to deploy to Kubernetes or any modern orchestrator, you’ll want health and readiness endpoints. These are tiny but very real examples of simple HTTP server examples in Go that integrate with modern infrastructure.
package main
import (
"fmt"
"log"
"net/http"
)
func healthHandler(w http.ResponseWriter, r *http.Request) {
// Liveness: is the process up?
w.WriteHeader(http.StatusOK)
fmt.Fprintln(w, "ok")
}
func readyHandler(w http.ResponseWriter, r *http.Request) {
// In a real app, you might check DB connectivity or other dependencies.
w.WriteHeader(http.StatusOK)
fmt.Fprintln(w, "ready")
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/healthz", healthHandler)
mux.HandleFunc("/readyz", readyHandler)
addr := ":8080"
log.Printf("Health server on %s", addr)
if err := http.ListenAndServe(addr, mux); err != nil {
log.Fatalf("server error: %v", err)
}
}
These endpoints show up in many of the best examples from real-world Go microservices, because they plug directly into readiness probes and load balancers.
For more context on service health and monitoring patterns, the U.S. Digital Service and related government tech groups publish guidance on reliable service operation; a good starting point is https://digital.gov/services/
Combining patterns: a small, realistic Go HTTP service
Now let’s combine several ideas into one small but realistic service. This is one of the more complete examples of simple HTTP server examples in Go you can still read in a single sitting:
- Multiple routes (
/,/api/time,/healthz). - JSON output.
- Basic logging middleware.
- Graceful shutdown.
package main
import (
"context"
"encoding/json"
"log"
"net/http"
"os"
"os/signal"
"syscall"
"time"
)
type TimeResponse struct {
Now time.Time `json:"now"`
Zone string `json:"zone"`
Unix int64 `json:"unix"`
}
func logging(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
next.ServeHTTP(w, r)
log.Printf("%s %s %s", r.Method, r.URL.Path, time.Since(start))
})
}
func home(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Go HTTP service example"))
}
func timeAPI(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
now := time.Now()
resp := TimeResponse{
Now: now,
Zone: now.Location().String(),
Unix: now.Unix(),
}
if err := json.NewEncoder(w).Encode(resp); err != nil {
http.Error(w, "could not encode JSON", http.StatusInternalServerError)
return
}
}
func health(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte("ok"))
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", home)
mux.HandleFunc("/api/time", timeAPI)
mux.HandleFunc("/healthz", health)
srv := &http.Server{
Addr: ":8080",
Handler: logging(mux),
}
go func() {
log.Printf("Server listening on %s", srv.Addr)
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatalf("listen: %v", err)
}
}()
stop := make(chan os.Signal, 1)
signal.Notify(stop, syscall.SIGINT, syscall.SIGTERM)
<-stop
log.Println("Shutdown signal received")
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := srv.Shutdown(ctx); err != nil {
log.Printf("shutdown error: %v", err)
} else {
log.Println("Server exited cleanly")
}
}
Among all the real examples of simple HTTP server examples in Go, this one hits the sweet spot: small enough to paste into a single file, but structured enough that you can grow it into a real service by adding database access, configuration, or authentication.
If you want to keep sharpening your Go skills beyond HTTP, universities like MIT and Stanford publish free material on systems programming and networking that pair nicely with Go’s standard library. For instance, see MIT OpenCourseWare: https://ocw.mit.edu/
FAQ: common questions about Go HTTP server examples
Are there other examples of simple HTTP server examples in Go using third-party routers?
Yes. While the standard library is more than enough for many cases, developers often reach for lightweight routers such as github.com/go-chi/chi or github.com/julienschmidt/httprouter when they want path parameters and cleaner route definitions. The underlying concepts are exactly the same as the examples shown above: you still implement handlers and pass them to an http.Server.
Can you give an example of handling query parameters in a Go HTTP server?
Here is a short example of reading a name query parameter:
func greetHandler(w http.ResponseWriter, r *http.Request) {
name := r.URL.Query().Get("name")
if name == "" {
name = "world"
}
fmt.Fprintf(w, "Hello, %s!", name)
}
You can attach this to any of the earlier examples of simple HTTP server examples in Go by registering it on a route like /greet.
How do I test these simple HTTP server examples in Go?
Go’s net/http/httptest package makes it straightforward to test handlers without running a real network server. You can create a httptest.NewRecorder() and an http.NewRequest, then call your handler directly and inspect the response. This mirrors how the standard library itself tests HTTP behavior.
Are these examples suitable for production use?
With some hardening, yes. Real production services add TLS termination (often via a reverse proxy like Nginx or a cloud load balancer), structured logging, metrics, and authentication. But the core patterns in these examples—http.Server, a ServeMux, JSON handlers, timeouts, and graceful shutdown—are the same ones used in many production Go systems today.
Related Topics
Practical examples of Go command-line application examples for 2025
Practical examples of 3 examples of working with Go maps for real projects
Practical examples of simple HTTP server examples in Go
Practical examples of defining and using structs in Go
Practical examples of creating and using Go interfaces
So You Think You Know HTTP in Go? Think Again
Explore More Go Code Snippets
Discover more examples and insights in this category.
View All Go Code Snippets