Fix 403 Forbidden for Swagger UI in Spring Boot Security
Fix 403 Forbidden for Swagger UI in Spring Boot by permitting /v3/api-docs/**, /swagger-ui/** and /webjars/** in Spring Security and handling CSRF for docs.
Why does Spring Boot return 403 Forbidden when accessing Swagger UI (http://localhost:9002/swagger-ui/index.html) despite permitting /swagger-ui/** and /v3/api-docs/** in Spring SecurityConfig?
Setup Details
- Spring Boot version: 3.4.2 (parent)
- Dependency:
org.springdoc:springdoc-openapi-starter-webmvc-ui:2.6.0
application.properties
springdoc.api-docs.enabled=true
springdoc.swagger-ui.enabled=true
Relevant SecurityConfig.java
http.authorizeHttpRequests(auth -> auth
.requestMatchers(
"/nonLoggedUsers/**","/pokemonData/**","/natureData/**",
"/itemData/allItems","/something_something/**",
"/v3/api-docs/**","/swagger-ui/**"
).permitAll()
.anyRequest().authenticated()
);
Other endpoints work correctly, but OpenAPI/Swagger paths are blocked with 403. How to fix this?
Spring Boot returns 403 Forbidden for Swagger UI most commonly because Spring Security is blocking the Swagger static assets or the OpenAPI endpoint (often the /webjars/** assets or index path) even when you permitted /swagger-ui/** and /v3/api-docs/**. Fix it by explicitly permitting the webjars path and exact index paths (e.g., /swagger-ui/index.html, /swagger-ui.html), and by handling CSRF for the docs endpoints (ignore CSRF for those paths or disable it in dev); matcher order matters. Apply the change in your SecurityFilterChain so the permitAll rules run before .anyRequest().authenticated().
Contents
- Why Spring Boot returns 403 Forbidden for Swagger UI
- Fix: update Spring SecurityConfig (code examples)
- CSRF and production-safe options
- Troubleshooting checklist & browser diagnostics
- Sources
- Conclusion
Why Spring Boot returns 403 Forbidden for Swagger UI
Short answer: the browser requests that render the UI are being blocked by Spring Security — usually static JS/CSS served from webjars or the API JSON at /v3/api-docs — so the UI can’t load and you see 403.
What actually happens (common causes)
- Missing /webjars/** permit: springdoc (and older Swagger setups) serves UI assets from webjars. If you only permit /swagger-ui/** but not /webjars/, the browser’s requests for swagger-ui JavaScript/CSS get 403 and the UI appears broken or forbidden. See the springdoc guidance and community reports describing exactly this symptom: missing /webjars/ produces 403 on static assets (springdoc GitHub, Stack Overflow example).
- Exact index path not matched: the UI page can be requested as /swagger-ui/index.html or legacy /swagger-ui.html; if your matcher set doesn’t include the exact path the initial page may be blocked.
- CSRF protection blocking API fetches: Swagger UI makes XHR/fetch requests to /v3/api-docs and to endpoints used by Try-it-out; if CSRF protection rejects those requests you’ll see 403 responses for POSTs or even GETs in some configurations (Stack Overflow example on CSRF causing 403).
- Matcher DSL / ordering issues: Spring Security evaluates matchers in order. The permitAll for Swagger must be present before a catch-all that requires authentication. Also make sure you’re using the right matcher DSL for your stack (servlet vs reactive).
Why this matters with your setup
- You’re on Spring Boot 3.4.2 with springdoc-openapi-starter-webmvc-ui 2.6.0 — springdoc expects the docs paths and static resources to be available, and the project docs explicitly call out permitting /v3/api-docs/** and /webjars/** when Spring Security is enabled (springdoc FAQ).
Fix: update Spring SecurityConfig (code examples)
Minimal and immediate change
- Add the webjars pattern and the index paths to your permit list. Example quick change that often resolves the 403:
http.authorizeHttpRequests(auth -> auth
.requestMatchers(
"/nonLoggedUsers/**","/pokemonData/**","/natureData/**",
"/itemData/allItems","/something_something/**",
"/v3/api-docs/**",
"/swagger-ui/**",
"/swagger-ui/index.html",
"/swagger-ui.html",
"/webjars/**"
).permitAll()
.anyRequest().authenticated()
);
Robust SecurityFilterChain example (servlet stack)
- This example adds explicit CSRF ignoring for doc endpoints (safer than disabling CSRF globally) and keeps your permit rules first:
import org.springframework.context.annotation.Bean;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
// Ignore CSRF for OpenAPI + Swagger static resources
http.csrf(csrf -> csrf.ignoringRequestMatchers(
new AntPathRequestMatcher("/v3/api-docs/**"),
new AntPathRequestMatcher("/swagger-ui/**"),
new AntPathRequestMatcher("/webjars/**")
));
http.authorizeHttpRequests(auth -> auth
.requestMatchers(
"/nonLoggedUsers/**","/pokemonData/**","/natureData/**",
"/itemData/allItems","/something_something/**",
"/v3/api-docs/**","/swagger-ui/**","/swagger-ui/index.html",
"/swagger-ui.html","/webjars/**"
).permitAll()
.anyRequest().authenticated()
);
// configure login/oauth as you need
return http.build();
}
Quick dev-only shortcut
- For local development you can temporarily disable CSRF entirely (not recommended for production):
http.csrf().disable();
Reactive apps
- If you’re on WebFlux, the equivalent is authorizeExchange().pathMatchers(…).permitAll() and you may need to tweak the CSRF handling accordingly.
Also include both /swagger-ui/** and /webjars/** — community reports and the springdoc repo call this out as the reason a UI returns 403 even when /swagger-ui/** looks permitted (Stack Overflow example, springdoc repo).
CSRF and production-safe options
You want the docs usable but not insecure. Options:
- Ignore CSRF only for the docs endpoints (shown above with AntPathRequestMatcher). That keeps CSRF for your app but lets Swagger UI fetch the OpenAPI JSON and perform try-it-out requests.
- Expose Swagger UI only for certain profiles (dev/staging). Put the swagger UI and springdoc.properties behind a profile: set springdoc.swagger-ui.enabled=true only for the dev profile and keep it off in production.
- Put the docs behind an authenticated admin page or network boundary (VPN/internal-only). Sometimes the simplest pattern is to keep swagger UI available only on non-prod ports or behind firewall rules.
Tip: the springdoc FAQ and repo discuss CSRF and recommend permitting the docs paths when security is present — check their guidance for the version you use (springdoc FAQ, springdoc repo).
Troubleshooting checklist & browser diagnostics
Before and after changes, do these checks:
- Browser devtools → Network tab
- Open http://localhost:9002/swagger-ui/index.html and watch which request returns 403: /v3/api-docs, /webjars/*, or the index itself?
- If /webjars/* is 403, add /webjars/** to your permit list.
- Direct curl tests
- curl -i http://localhost:9002/swagger-ui/index.html
- curl -i http://localhost:9002/v3/api-docs
- curl -i http://localhost:9002/webjars/swagger-ui/swagger-ui-bundle.js
Those will show whether the resource itself is blocked.
- Enable Spring Security debug logging temporarily
- application.properties: logging.level.org.springframework.security=DEBUG
- Look for how matchers are evaluated and which rule returns 403.
- Verify matcher DSL and ordering
- Ensure your permitAll requestMatchers are registered before anyRequest().authenticated().
- If you have multiple SecurityFilterChain beans, confirm the chain that handles swagger paths is active and ordered correctly.
- Check for other filters (CORS, custom filters) that might return 403 for preflight or XHR.
- Verify springdoc version behavior
- Some springdoc releases moved assets; check the repo or changelog for your 2.6.0 version if behavior differs (springdoc issues).
If you still see 403 after permitting /webjars/, /swagger-ui/ and handling CSRF, capture the exact failing request URL and paste it into a browser or curl — that usually points straight to the missing permit rule.
Sources
- Allow anonymous access to springdoc-openapi-ui with Spring Security - Stack Overflow
- springdoc-openapi (GitHub)
- springdoc F.A.Q.
- Swagger POST return 403 Forbidden - Stack Overflow
- Cannot solve 403 Forbidden when using open API - Stack Overflow
- @Hidden Annotation Causes Swagger UI to Fail with “403 Forbidden” - springdoc issue
- Swagger UI empty and gives 403 - Stack Overflow
Conclusion
The 403 Forbidden for Swagger UI in Spring Boot is almost always caused by Spring Security blocking static assets or the OpenAPI endpoint — add explicit permits for /webjars/** and the exact index paths, and handle CSRF for the docs endpoints (ignore CSRF for /v3/api-docs/** and related paths or disable it only in dev). With those changes to your Spring SecurityConfig the springdoc OpenAPI Swagger UI should load normally.