Documentation
¶
Overview ¶
Package rulefiles is the root of a module that parses the _headers and _redirects files of a static site build, the formats popularised by Netlify and Cloudflare Pages.
The root package holds no code of its own; the two formats are implemented by dedicated packages:
- thde.io/rulefiles/header reads a _headers file and resolves the response header fields declared for a request path.
- thde.io/rulefiles/redirect reads a _redirects file and resolves the destination a request is redirected or rewritten to.
Each package can resolve rules individually for callers that act on the result directly, or wrap a net/http.Handler for middleware use.
Middleware Pipeline ¶
When serving a static site with both header and redirect rules, the handlers compose with header rules wrapped innermost, directly around the file server, and redirect rules outermost:
site, err := header.NewHandler(headersFile, http.FileServerFS(build))
if err != nil {
return err
}
site, err = redirect.NewHandler(redirectsFile, site)
if err != nil {
return err
}
This ensures that when a redirect rule rewrites a request (HTTP status 200), the header rules apply to the rewritten destination path rather than the originally requested URL.
Pattern Syntax ¶
Both rule formats share the same path pattern syntax:
- Patterns match against the decoded request path and are case-sensitive.
- Named placeholders (":name") match a single path segment and capture its value.
- A trailing splat ("*") matches the remainder of the path and is captured as ":splat".
- Comments begin with "#" at the start of a line or following whitespace.
Error Handling ¶
Parser functions report all errors across the file together using errors.Join. Every error wraps one of two sentinel errors:
- thde.io/rulefiles/header.ErrSyntax / thde.io/rulefiles/redirect.ErrSyntax: for malformed lines that violate the file format syntax.
- thde.io/rulefiles/header.ErrUnsupported / thde.io/rulefiles/redirect.ErrUnsupported: for valid platform syntax that this Go module does not implement (such as conditions on country, language, or cookie, or response framing headers).
Example ¶
Example combines redirect and header rules.
package main
import (
"context"
"fmt"
"log/slog"
"net/http"
"net/http/httptest"
"strings"
"thde.io/rulefiles/header"
"thde.io/rulefiles/redirect"
)
func main() {
const (
redirectsFile = "/old/* /new/:splat\n/app/* /index.html 200\n/gone / 410\n"
headersFile = "/*\n X-Robots-Tag: noindex\n/new/*\n Cache-Control: no-store\n"
)
// files simulates a static file server.
files := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
//nolint:gosec // Echo path for example output.
_, _ = fmt.Fprint(w, "the file at "+r.URL.Path)
})
// The header rules apply to the path a request is rewritten to, so they wrap
// the file server rather than the site.
served, err := header.NewHandler(strings.NewReader(headersFile), files)
if err != nil {
slog.Error("reading the header rules", "error", err)
return
}
site, err := redirect.NewHandler(strings.NewReader(redirectsFile), served)
if err != nil {
slog.Error("reading the redirect rules", "error", err)
return
}
for _, request := range []string{"/index.html", "/old/page.html", "/new/page.html", "/app/deep/link", "/gone"} {
rec := httptest.NewRecorder()
site.ServeHTTP(rec, httptest.NewRequestWithContext(context.Background(), http.MethodGet, request, http.NoBody))
fmt.Printf("%s: %d location=%q cache-control=%q robots=%q\n",
request, rec.Code, rec.Header().Get("Location"),
rec.Header().Get("Cache-Control"), rec.Header().Get("X-Robots-Tag"))
}
}
Output: /index.html: 200 location="" cache-control="" robots="noindex" /old/page.html: 301 location="/new/page.html" cache-control="" robots="" /new/page.html: 200 location="" cache-control="no-store" robots="noindex" /app/deep/link: 200 location="" cache-control="" robots="noindex" /gone: 410 location="" cache-control="" robots=""
Directories
¶
| Path | Synopsis |
|---|---|
|
Package header parses and applies _headers files.
|
Package header parses and applies _headers files. |
|
internal
|
|
|
rulefile
Package rulefile implements the syntax shared by the _redirects and _headers files, the formats popularised by Netlify and Cloudflare Pages: comments, path patterns and the ":name" placeholders a pattern captures.
|
Package rulefile implements the syntax shared by the _redirects and _headers files, the formats popularised by Netlify and Cloudflare Pages: comments, path patterns and the ":name" placeholders a pattern captures. |
|
Package redirect parses and resolves _redirects files.
|
Package redirect parses and resolves _redirects files. |