Skip to content

Routes reference

What Register mounts, what each route answers with, and which headers come back. Paths below use the defaults; substitute whatever you passed to WithSpecPath and WithDocsPath.

What Register mounts on the mux

Three patterns, in net/http 1.22 method-pattern form:

Pattern Handler
GET /openapi.yaml writes the spec bytes you passed to Register
GET /docs/{$} the generated Stoplight index HTML
GET /docs/ http.FileServer over the embedded assets, with the docs prefix stripped

{$} matches the docs path exactly and nothing below it, so the index page and the asset subtree do not collide. Each Register call is independent: two calls with different paths mount six routes and serve two API versions side by side. Two calls sharing a path panic; see what happens when a path is wrong.

Which methods each route answers

Patterns are registered for GET only, and net/http matches HEAD against a GET pattern automatically.

Request Response
GET, HEAD 200, as described below
POST, PUT, DELETE, OPTIONS, anything else 405 Method Not Allowed, Allow: GET, HEAD

The 405 comes from ServeMux, not from this module. There is no OPTIONS handling and no CORS preflight support. The docs are designed to be served same-origin with the API, so there is nothing to preflight.

GET /openapi.yaml: the spec

Returns the spec byte slice verbatim with Content-Type: application/yaml and no other body processing: no parsing, no validation, no reformatting.

Content-Type is application/yaml unconditionally. A JSON OpenAPI document is still served, and still renders in the UI, but with a YAML content type. JSON is a subset of YAML, so parsers accept it, but a client keying off the content type will be misled.

A nil or empty spec is not an error: the route registers and returns 200 with an empty body. The docs page then loads and fails to find a document to render.

GET /docs/: the generated page

Returns 200 with Content-Type: text/html; charset=utf-8 and a small HTML document (around 530 bytes) that loads the embedded Stoplight assets and mounts one <elements-api> element:

<elements-api
  apiDescriptionUrl="/openapi.yaml"
  router="hash"
  layout="sidebar"
  tryItCredentialsPolicy="same-origin"
></elements-api>

Only apiDescriptionUrl and the page <title> vary. They come from WithSpecPath and WithTitle. router, layout and tryItCredentialsPolicy are fixed; there is no option to change them.

router="hash" means deep links into the reference are fragments (/docs/#/paths/v1-greeting/get), so navigation inside the UI never reaches your server, and the fragment is never sent with the request.

A request for /docs without the trailing slash is redirected by ServeMux with 307 Temporary Redirect to /docs/.

GET /docs/{asset}: the embedded UI

http.FileServer over the three embedded files:

Asset Size Content-Type
web-components.min.js about 2.1 MB text/javascript; charset=utf-8
web-components.min.js.LICENSE.txt about 4 KB text/plain; charset=utf-8
styles.min.css about 300 KB text/css; charset=utf-8

The .LICENSE.txt file carries the notices for the libraries bundled into web-components.min.js, whose first line points a reader at it.

Anything else under the prefix is 404. /docs/index.html is redirected to ./ by http.FileServer's own index handling, which lands on the generated page.

Responses carry no cache validators. The embedded filesystem reports a zero modification time, so http.ServeContent emits neither Last-Modified nor ETag, and nothing sets Cache-Control. A conditional request is answered 200 with the full body rather than 304, so every uncached load of the docs page transfers the whole 2.4 MB again. Put a caching proxy or a Cache-Control-setting middleware in front if that traffic matters. This module does not do it for you.

Response headers

Every one of the three routes goes through the same security-header middleware unless you passed WithoutSecurityHeaders:

Header Default value
X-Content-Type-Options nosniff
X-Frame-Options DENY
Content-Security-Policy frame-ancestors 'none'
Referrer-Policy no-referrer
Strict-Transport-Security not set; HSTS is opt-in

The Stoplight console renders and issues try-it requests normally under these defaults; they restrict framing and sniffing, not scripting.

X-Frame-Options: DENY and frame-ancestors 'none' do mean the docs page cannot be embedded in an <iframe> on another page, including your own portal. Relax it with WithSecurityHeaderOptions if you need that.

Replacing the policy with a full CSP is a sharper edge: WithContentSecurityPolicy replaces the whole header, and the generated page carries an inline style="height: 100vh; margin: 0" attribute on <body> while Stoplight injects styles at runtime. A policy without 'unsafe-inline' in style-src will break the page's layout.