Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions dockerfiles/nginx/proxito.conf.template
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ server {
add_header X-Content-Type-Options $x_content_type_options always;

# https://docs.djangoproject.com/en/4.2/ref/middleware/#cross-origin-opener-policy
proxy_hide_header Cross-Origin-Opener-Policy;
set $cross_origin_opener_policy $upstream_http_cross_origin_opener_policy;
Comment on lines +117 to 118
Copy link
Member

@humitos humitos Feb 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand how this works.

I tested your PR locally and I still see the header same-origin:

  agj/cross-origin-opener-policy ✔
$ curl -ILs http://test-builds.devthedocs.org/en/latest/ | grep -i origin-opener
Cross-Origin-Opener-Policy: same-origin

On the other hand, we don't want to always hide this header. We still want to have the same-origin default that Django adds. However, we want to be able to override it for a particular project to change its value or delete it. Otherwise, it will be a security issue.

Copy link
Contributor Author

@agjohnson agjohnson Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I already noted above the header doesn't seem to return correctly.

The logic here is the same logic as the other headers, I didn't do anything special.

proxy_hide_header Content-Security-Policy;
set $content_security_policy $upstream_http_content_security_policy;
add_header Content-Security-Policy $content_security_policy always;

This does need to conditionally pass through the default, but this was a quick effort and didn't get this to even replace this header value yet.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I wasn't aware of the other proxy_hide_header.

Reading more here, we use it because we want to remove the headers from S3 (MinIO in development) due that it sets some headers we want to force coming from the upstream instead of the proxy pass. There are a bunch of headers where we don't use proxy_hide_header and that's why because S3 is not setting them, so there is nothing to hide first.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I get that usage, there are a number of cases of proxy_hide_header in that config too though.

I expected proxy_hide_header here to also hide the Cross-Origin-Opener-Policy header set by Django, but it doesn't seem to.

Ultimately what we need is to replace the default header with the custom header but only when it exists. But neither replacing or hiding the header worked just yet.

add_header Cross-Origin-Opener-Policy $cross_origin_opener_policy always;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Generated by Django 5.2.8 on 2026-02-14 02:59

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("projects", "0158_add_search_subproject_filter_option"),
]

operations = [
migrations.AlterField(
model_name="httpheader",
name="name",
field=models.CharField(
choices=[
("access_control_allow_origin", "Access-Control-Allow-Origin"),
("access_control_allow_headers", "Access-Control-Allow-Headers"),
("access_control_expose_headers", "Access-Control-Expose-Headers"),
("content_security_policy", "Content-Security-Policy"),
("cross_origin_opener_policy", "Cross-Origin-Opener-Policy"),
("feature_policy", "Feature-Policy"),
("permissions_policy", "Permissions-Policy"),
("referrer_policy", "Referrer-Policy"),
("x_frame_options", "X-Frame-Options"),
("x_content_type_options", "X-Content-Type-Options"),
],
max_length=128,
),
),
]
1 change: 1 addition & 0 deletions readthedocs/projects/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2028,6 +2028,7 @@ class HTTPHeader(TimeStampedModel, models.Model):
("access_control_allow_headers", "Access-Control-Allow-Headers"),
("access_control_expose_headers", "Access-Control-Expose-Headers"),
("content_security_policy", "Content-Security-Policy"),
("cross_origin_opener_policy", "Cross-Origin-Opener-Policy"),
("feature_policy", "Feature-Policy"),
("permissions_policy", "Permissions-Policy"),
("referrer_policy", "Referrer-Policy"),
Expand Down