Skip to content

fix(swagger-ui): Use relative redirect to add trailing slash#1530

Open
boblehest wants to merge 1 commit intojuhaku:masterfrom
boblehest:swagger-relative-redirect
Open

fix(swagger-ui): Use relative redirect to add trailing slash#1530
boblehest wants to merge 1 commit intojuhaku:masterfrom
boblehest:swagger-relative-redirect

Conversation

@boblehest
Copy link
Copy Markdown

Given SwaggerUi::new("/path"), we would previously redirect requests for /path to /path/ (adding the trailing slash). Using an absolute redirect path like this can cause issues when the HTTP server is behind a proxy doing path rewriting.

Example scenario detailing the issue:

Say you have a Rust application which exposes a HTTP server with utoipa-swagger-ui at path /path/to/swagger/. The server is available at hostname my.server.

You also have a proxy server at my.proxy, which handles incoming requests to the path /api/... by stripping the path prefix /api, and forwarding the request to my.server.

Then you do the following:

  1. You send a request to my.proxy/api/path/to/swagger
  2. The server at my.proxy forwards the request to my.server/path/to/swagger (note the stripped /api prefix)
  3. The server at my.server redirects it to /path/to/swagger/ (to "add a trailing slash")
  4. The client then follows this redirect, sending a new request to my.proxy/path/to/swagger/
  5. The request fails, because the proxy does not serve anything at this path (the path has no /api/ prefix).

Solution:

Redirecting to the relative path swagger/* should be more robust, as it more precisely expresses the intent of simply adding a slash to the end of the path, instead of replacing the entire path.

*Or more generally, redirecting to X/ where X is the last path segment of the configured swagger-ui path.

Given SwaggerUi::new("/path"), we would previously redirect requests
for `/path` to `/path/` (adding the trailing slash). Using an absolute
redirect path like this can cause issues when the HTTP server is behind
a proxy doing path rewriting.

Example scenario detailing the issue:

Say you have a Rust application which exposes a HTTP server with
`utoipa-swagger-ui` at path `/path/to/swagger/`. The server is
available at hostname `my.server`.

You also have a proxy server at `my.proxy`, which handles incoming requests
to the path `/api/...` by stripping the path prefix `/api`, and
forwarding the request to `my.server`.

Then you do the following:

1. You send a request to `my.proxy/api/path/to/swagger`
2. The server at `my.proxy` forwards the request to
   `my.server/path/to/swagger` (note the stripped `/api` prefix)
3. The server at `my.server` redirects it to `/path/to/swagger/`
   (to "add a trailing slash")
4. The client then follows this redirect, sending a new request to
   `my.proxy/path/to/swagger/`
5. The request fails, because the proxy does not serve anything at this
   path (the path has no `/api/` prefix).

Solution:

Redirecting to the relative path `swagger/`* should be more robust,
as it more precisely expresses the intent of simply adding a slash to
the end of the path, instead of replacing the entire path.

*Or more generally, redirecting to `X/` where `X` is the last path
segment of the configured swagger-ui path.
@boblehest
Copy link
Copy Markdown
Author

boblehest commented Mar 12, 2026

There are some previous issues which discuss serving SwaggerUi behind a proxy: #842 and #856 .

I've personally had two issues when trying to serve it being a proxy that does path rewriting.

This PR fixes one of them (people forgetting to add the trailing slash, getting redirected to a broken URL, then coming to ask me why my API doesn't host SwaggerUi).

The other problem I've run into is SwaggerUi not finding openapi.json, which I work around by following the hint from this comment in issue 856:

SwaggerUi::new("/swagger-ui")
    .url("/api-docs/openapi.json", ApiDoc::openapi())
    .config(utoipa_swagger_ui::Config::new(["../api-docs/openapi.json"])),
                                           // ^-- override to use relative path

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant