The Security Audit That Found 4 Issues in One Weekend A developer found four security issues during a weekend audit of the jo4 URL shortener, including Spring Security's default X-Frame-Options header breaking embeddable widgets and a stack trace leak via unchecked query parameters. The developer fixed the issues by taking surgical control of security headers and parsing parameters safely. This article was originally published on Jo4 Blog . I spent a weekend doing a security audit on jo4. I expected to find maybe one minor issue. I found four, and one of them was leaking stack traces to users. Here's the full damage report. Spring Security's default security headers are great — until they break your features. Blanket X-Frame-Options: DENY kills embeddable widgets, unchecked query parameters leak stack traces, and default CSP can conflict with your custom headers. The fix isn't disabling security — it's taking surgical control. Spring Security auto-injects X-Frame-Options: DENY on every response. Every. Single. One. For most endpoints, that's exactly what you want. You don't want someone embedding your login page in a malicious iframe. But jo4 has embed widgets — public stats dashboards that users can iframe into their own sites. // What Spring Security was doing to ALL responses X-Frame-Options: DENY This meant every embed widget was broken. Browsers refused to render them in iframes. The feature existed, the code worked, but Spring Security was silently killing it at the HTTP header level. The wrong fix would be to just disable X-Frame-Options globally. That opens you up to clickjacking on every page. The right fix: disable Spring's blanket header and delegate the decision to the controller that actually knows what should be embeddable. @Configuration public class SecurityConfig { @Bean public SecurityFilterChain filterChain HttpSecurity http throws Exception { http .headers h - h.frameOptions f - f.disable // ... rest of security config ; return http.build ; } } Then in EmbedController , set the header dynamically based on whether the entity has enablePublicStats turned on: @GetMapping "/embed/stats/{shortCode}" public ResponseEntity