HTTP 404 when text/html is not first in accept request header

AI-generated summary

A bug has been reported where Discourse returns HTTP 404 when text/html is not the first MIME type in the Accept header (e.g., Accept: text/plain, text/html), while Accept: text/html, text/plain returns 200 OK. tgm initially observed this behavior on meta.discourse.org. sam noted this likely affects API consumers and welcomed a PR to fix it. abdallahalsamman tested locally and found no issue when hitting the Rails process directly or via Express proxy, suggesting the problem may lie in the production setup — possibly NGINX or Docker configuration. sam advised debugging by bypassing the Express proxy and testing a local production Docker setup. abdallahalsamman confirmed that direct requests to the Rails server (port 3000) return 200 OK regardless of Accept header order, reinforcing the theory that the issue is introduced by infrastructure layers (e.g., NGINX or proxy). The bug appears environment-specific and not inherent to Rails or Discourse core.

The order of the MIME types in the accept request header as sent to the Discourse server seems to matter:

curl -i -H 'Accept: text/plain, text/html' https://meta.discourse.org
HTTP/2 404 
...
curl -i -H 'Accept: text/html, text/plain' https://meta.discourse.org
HTTP/2 200
...
4 Likes

This would mainly impact API consumers I guess. Open to a PR that corrects this.

4 Likes

I tried this on the dev build, I’m getting 200 OK from Rails::WelcomeController#index for both headers Accept: text/html, text/plain and Accept: text/plain, text/html.

Started GET "/" for 127.0.0.1 at 2023-03-30 15:52:45 +0300
Processing by Rails::WelcomeController#index as TEXT
  Rendering /Users/machine/.rbenv/versions/3.2.1/lib/ruby/gems/3.2.0/gems/railties-7.0.4.3/lib/rails/templates/rails/welcome/index.html.erb
  Rendered /Users/machine/.rbenv/versions/3.2.1/lib/ruby/gems/3.2.0/gems/railties-7.0.4.3/lib/rails/templates/rails/welcome/index.html.erb (Duration: 0.1ms | Allocations: 36)
Completed 200 OK in 1ms (Views: 1.3ms | ActiveRecord: 0.0ms | Allocations: 796)
machine@machines-MacBook-Pro discourse % curl -I -H 'Accept: text/html, text/plain' http://127.0.01:4200 
HTTP/1.1 200 OK
X-Powered-By: Express
cache-control: no-store, must-revalidate, private, max-age=0
connection: close
content-security-policy: base-uri 'self'; object-src 'none'; script-src http://127.0.01:4200/assets/ http://127.0.01:4200/ember-cli-live-reload.js http://127.0.01:4200/_lr/ http://127.0.01:4200/logs/ http://127.0.01:4200/sidekiq/ http://127.0.01:4200/mini-profiler-resources/ http://127.0.01:4200/assets/ http://127.0.01:4200/brotli_asset/ http://127.0.01:4200/extra-locales/ http://127.0.01:4200/highlight-js/ http://127.0.01:4200/javascripts/ http://127.0.01:4200/plugins/ http://127.0.01:4200/theme-javascripts/ http://127.0.01:4200/svg-sprite/ 'unsafe-eval' http://127.0.01:4200/ember-cli-live-reload.js http://127.0.01:4200/_lr/ 'sha256-Gty3/aPWFfSvz7pdT39HY97/+2opLup9V0L19ZF0IwY='; worker-src 'self' http://127.0.01:4200/assets/ http://127.0.01:4200/brotli_asset/ http://127.0.01:4200/javascripts/ http://127.0.01:4200/plugins/; frame-ancestors 'self'; manifest-src 'self'
Content-Type: text/html; charset=utf-8
date: Thu, 30 Mar 2023 12:51:19 GMT
referrer-policy: strict-origin-when-cross-origin
set-cookie: __profilin=p%3Dt; path=/; HttpOnly; SameSite=Lax
Vary: Accept, Accept-Encoding
x-content-type-options: nosniff
x-discourse-route: list/latest
x-download-options: noopen
x-frame-options: SAMEORIGIN
x-miniprofiler-ids: 3uaox120htfj3nkrwxvy,v4ylpbinbd6pz2p7ar2l,8snf28djimkyzf5vpe5v,iq2fhxx37pkunle3zzxv,o5b5ae8lxmj9vgazlydh,spjovt9bn2x0ziaikfxh,ut7qvfhrp7pt2hflvf1r,m428xr7orlndjj5fm17n,eryv846zt9oqyiccvw4,t8fp5g9iqbfxrmkd3qv9,jea1zbtm7vu2zbmb1kdk,u5h4t1804men5vx9xuvd
x-miniprofiler-original-cache-control: no-cache, no-store
x-permitted-cross-domain-policies: none
x-request-id: be9de952-9043-41a9-adca-1c5124b1ff90
x-xss-protection: 0
content-encoding: null
Content-Length: 0
ETag: W/"0-2jmj7l5rSw0yVb/vlWAYkK/YBwk"

machine@machines-MacBook-Pro discourse % curl -I -H 'Accept: text/plain, text/html' http://127.0.01:4200 
HTTP/1.1 200 OK
X-Powered-By: Express
cache-control: no-store, must-revalidate, private, max-age=0
connection: close
content-security-policy: base-uri 'self'; object-src 'none'; script-src http://127.0.01:4200/assets/ http://127.0.01:4200/ember-cli-live-reload.js http://127.0.01:4200/_lr/ http://127.0.01:4200/logs/ http://127.0.01:4200/sidekiq/ http://127.0.01:4200/mini-profiler-resources/ http://127.0.01:4200/assets/ http://127.0.01:4200/brotli_asset/ http://127.0.01:4200/extra-locales/ http://127.0.01:4200/highlight-js/ http://127.0.01:4200/javascripts/ http://127.0.01:4200/plugins/ http://127.0.01:4200/theme-javascripts/ http://127.0.01:4200/svg-sprite/ 'unsafe-eval' http://127.0.01:4200/ember-cli-live-reload.js http://127.0.01:4200/_lr/ 'sha256-Gty3/aPWFfSvz7pdT39HY97/+2opLup9V0L19ZF0IwY='; worker-src 'self' http://127.0.01:4200/assets/ http://127.0.01:4200/brotli_asset/ http://127.0.01:4200/javascripts/ http://127.0.01:4200/plugins/; frame-ancestors 'self'; manifest-src 'self'
Content-Type: text/html; charset=utf-8
date: Thu, 30 Mar 2023 12:51:26 GMT
referrer-policy: strict-origin-when-cross-origin
set-cookie: __profilin=p%3Dt; path=/; HttpOnly; SameSite=Lax
Vary: Accept, Accept-Encoding
x-content-type-options: nosniff
x-download-options: noopen
x-frame-options: SAMEORIGIN
x-miniprofiler-ids: fw6wmg2wtmnuc4qqk5ri,3uaox120htfj3nkrwxvy,v4ylpbinbd6pz2p7ar2l,8snf28djimkyzf5vpe5v,iq2fhxx37pkunle3zzxv,o5b5ae8lxmj9vgazlydh,spjovt9bn2x0ziaikfxh,ut7qvfhrp7pt2hflvf1r,m428xr7orlndjj5fm17n,eryv846zt9oqyiccvw4,t8fp5g9iqbfxrmkd3qv9,jea1zbtm7vu2zbmb1kdk,u5h4t1804men5vx9xuvd
x-permitted-cross-domain-policies: none
x-request-id: 00c3add3-15ec-42d5-9c82-9d6ce09b09fd
x-xss-protection: 0
content-encoding: null
Content-Length: 0
ETag: W/"0-2jmj7l5rSw0yVb/vlWAYkK/YBwk"

machine@machines-MacBook-Pro discourse % 

@sam can you please provide some direction on how to fix this?

This is not going to be easy to debug, it may be related to the NGINX configuration in production, maybe this is in Rails I am not sure.

I would try to first make the request direct to the Rails process and not to the express proxy, if that works then I would next debug a production docker setup on local.

Yep, sending the requests directly to the Rails process doesn’t reproduce the issue either

machine@machines-MacBook-Pro discourse % curl -I -H 'Accept: text/plain, text/html' http://127.0.0.1:3000
HTTP/1.1 200 OK
Date: Fri, 31 Mar 2023 00:17:12 GMT
Connection: close
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 0
X-Content-Type-Options: nosniff
X-Download-Options: noopen
X-Permitted-Cross-Domain-Policies: none
Referrer-Policy: strict-origin-when-cross-origin
Content-Type: text/html; charset=utf-8
Vary: Accept
Content-Security-Policy: base-uri 'self'; object-src 'none'; script-src http://127.0.0.1:3000/logs/ http://127.0.0.1:3000/sidekiq/ http://127.0.0.1:3000/mini-profiler-resources/ http://127.0.0.1:3000/assets/ http://127.0.0.1:3000/brotli_asset/ http://127.0.0.1:3000/extra-locales/ http://127.0.0.1:3000/highlight-js/ http://127.0.0.1:3000/javascripts/ http://127.0.0.1:3000/plugins/ http://127.0.0.1:3000/theme-javascripts/ http://127.0.0.1:3000/svg-sprite/ 'unsafe-eval' http://127.0.0.1:3000/ember-cli-live-reload.js http://127.0.0.1:3000/_lr/ 'sha256-Gty3/aPWFfSvz7pdT39HY97/+2opLup9V0L19ZF0IwY='; worker-src 'self' http://127.0.0.1:3000/assets/ http://127.0.0.1:3000/brotli_asset/ http://127.0.0.1:3000/javascripts/ http://127.0.0.1:3000/plugins/; frame-ancestors 'self'; manifest-src 'self'
X-Request-Id: 9a58c127-bc9e-4c27-a6a4-2ac5b9d34e5f
Cache-Control: no-store, must-revalidate, private, max-age=0
X-MiniProfiler-Ids: vit385ud6hkrs02yr6r,3izsng340krymolreb78,cq88v42hj61giemunux3,dq9oli1nmmat6k4o274k,37obrexoyg8146xozpqg,qucwk4opydg3vjyj3kz2,b54tne0pbtipq7rie4j9,m2syvsy4jl6wi0q3c2g5,72du4xsbgjmr67p4mhdx,zw5wg2t6susof6zele0y,kar63by1273tae1l0qgw
Set-Cookie: __profilin=p%3Dt; path=/; HttpOnly; SameSite=Lax

machine@machines-MacBook-Pro discourse % curl -I -H 'Accept: text/html, text/plain' http://127.0.0.1:3000
HTTP/1.1 200 OK
Date: Fri, 31 Mar 2023 00:17:15 GMT
Connection: close
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 0
X-Content-Type-Options: nosniff
X-Download-Options: noopen
X-Permitted-Cross-Domain-Policies: none
Referrer-Policy: strict-origin-when-cross-origin
X-Discourse-Route: list/latest
Content-Type: text/html; charset=utf-8
Vary: Accept
Cache-Control: no-store, must-revalidate, private, max-age=0
Content-Security-Policy: base-uri 'self'; object-src 'none'; script-src http://127.0.0.1:3000/logs/ http://127.0.0.1:3000/sidekiq/ http://127.0.0.1:3000/mini-profiler-resources/ http://127.0.0.1:3000/assets/ http://127.0.0.1:3000/brotli_asset/ http://127.0.0.1:3000/extra-locales/ http://127.0.0.1:3000/highlight-js/ http://127.0.0.1:3000/javascripts/ http://127.0.0.1:3000/plugins/ http://127.0.0.1:3000/theme-javascripts/ http://127.0.0.1:3000/svg-sprite/ 'unsafe-eval' http://127.0.0.1:3000/ember-cli-live-reload.js http://127.0.0.1:3000/_lr/ 'sha256-Gty3/aPWFfSvz7pdT39HY97/+2opLup9V0L19ZF0IwY='; worker-src 'self' http://127.0.0.1:3000/assets/ http://127.0.0.1:3000/brotli_asset/ http://127.0.0.1:3000/javascripts/ http://127.0.0.1:3000/plugins/; frame-ancestors 'self'; manifest-src 'self'
X-Request-Id: 8ac7c6b7-512e-4c1b-b868-88f3a1ea200c
X-MiniProfiler-Original-Cache-Control: no-cache, no-store
X-MiniProfiler-Ids: tae1e7defycsvswht8d2,vit385ud6hkrs02yr6r,3izsng340krymolreb78,cq88v42hj61giemunux3,dq9oli1nmmat6k4o274k,37obrexoyg8146xozpqg,qucwk4opydg3vjyj3kz2,b54tne0pbtipq7rie4j9,m2syvsy4jl6wi0q3c2g5,72du4xsbgjmr67p4mhdx,zw5wg2t6susof6zele0y,kar63by1273tae1l0qgw
Set-Cookie: __profilin=p%3Dt; path=/; HttpOnly; SameSite=Lax

I will investigate further with a production docker setup on local :+1:

1 Like