About HTTP header
When a browser opens a file or page on the web, the browser makes HTTP request as shown below and sends it to the Web server.
GET HTTP/2
scheme: https
host: alvine.org
filename: /
The web server responds to this by adding the response body (data of the page body) to the HTTP response header as shown below. It is case-insensitive.
HTTP/2 200 OK
alt-svc: h3=":443"; ma=2592000
cache-control: max-age=15768000
content-encoding: gzip
content-security-policy: default-src 'self'; frame-ancestors 'none';
content-type: text/html; charset=utf-8
cross-origin-embedder-policy: require-corp
cross-origin-opener-policy: same-origin
cross-origin-resource-policy: same-origin
etag: "d8uywr2ck2s594x-gzip"
last-modified: Tue, 01 Apr 2025 03:05:00 GMT
permissions-policy browsing-topics=(),interest-cohort=(),join-ad-interest-group=(),run-ad-auction=(),attribution-reporting=()
referrer-policy: strict-origin-when-cross-origin
server: Caddy
strict-transport-security: max-age=15768000;
vary: Accept-Encoding
x-content-type-options: nosniff
x-frame-options: DENY
content-length: 3442
date: Tue, 01 Apr 2025 12:34:19 GMT
The content of the response header can be modified by the web server administrator, and although many sites leave it at the default of the web server application, it can contain important information that is useful to the user. I would like to introduce what I consider to be particularly important.
Security
Cross-Origin- or something like that.
The word “security” sounds exaggerated, but it is all up to the browser to decide how to handle the response, so it is not a matter of setting this much and you are absolutely safe. It is necessary to understand that this is essentially just a “request” from the server.
Content-Security-Policy (CSP)
Content-Security-Policy: default-src 'self'; frame-ancestors 'none';
Header to restrict loading of third-party elements, useful for preventing XSS (cross-site scripting).
If default-src 'self'
is set, elements not
hosted on the same host as the page (web fonts, embedded SNS
or share button, etc.) are blocked, as well as inline
<style>
and <script>
elements. Most websites will be rendered useless, and you
rarely see a site that uses only self
.
Small personal web sites can easily implement it, but if they don’t do dynamic page generation and have no way to introduce XSS, like this site, it’s not worth it.
The well-known x-frame-options: DENY
, which
prevents clickjacking, has already been deprecated and its
function has been replaced by
frame-ancestors ‘none’
.
CSP is a very deep feature, but it is far beyond my skill.
For now, it might be a good idea to set
default-src
.
Cross-Origin-Resource-Policy (CORP)
Cross-Origin-Resource-Policy: same-origin
Restrict loading of this site’s resources from another
origin (another site). The default is
cross-origin
, which allows requests from any
origin to load resources. If it is set to
same-origin
or same-site
, then
resources cannot be loaded directly from other sites. However,
since blocking loading is a browser-side function, it does not
necessarily mean that the response body will not be sent.
Cross-Origin-Embedder-Policy (COEP)
Cross-Origin-Embedder-Policy: require-corp
Restrict resources other than the specified origin from
being loaded on this site. The default is
unsafe-none
, in which case cross-origin resources
can be loaded without explicit permission in CORP. If set to
require-corp
, it will block reading of any origin
other than the one explicitly specified in CORP.
Cross-Origin-Opener-Policy (COOP)
Cross-Origin-Opener-Policy: same-origin
Restrict the site from sharing a browsing context with
another origin. The default is unsafe-none
, which
means that when another origin is opened from this site, the
opened origin can control the parent window using JavaScript’s
window.opener
. If same-origin
is
used, the browsing context is not shared between origins, so
they cannot communicate with each other. It is better to set
same-origin
when there is no need to be
controlled by other sites.
same-origin-allow-popups
is used for kind of a
payment required services.
Strict-Transport-Security (HSTS)
Strict-Transport-Security: max-age=15768000;
Forces the browser to redirect to HTTPS when it sends
request over HTTP. The redirection to HTTPS for this site will
be in effect for the specified number of seconds. Common
examples are max-age=15768000
for half a year and
max-age=31536000
for one year. However, it is
meaningless if it is not cached in the browser. So no matter
how long max-age is set, it is meaningless for the first
access or access in secret mode.
X-Content-Type-Options
X-Content-Type-Options nosniff
Prevent the browser from sniffing the MIME type of a file.
Headers beginning with X-
are either private,
non-standardized headers, or were once non-standardized but
are now standardized. The latter is the case. To begin with,
starting a private header with X-
has already
been deprecated1.
Privacy
Permissions-Policy
Permissions-Policy: browsing-topics=(), interest-cohort=(), join-ad-interest-group=(), run-ad-auction=(), attribution-reporting=()
This is the core of the “kind HTTP header” that I wanted to convey in this article.
Permissions-Policy is a header that restricts the use of location information and cameras from the website side, but this is not included in this article because the default browser setting should ask for permission and such a setting is not used for personal sites. There is a more important setting than that. Opting-out of Google’s privacy sandbox.
Privacy Sandbox
Privacy Snadbox is the technology to track users “on the device side, not on the server side” by Google, which has finally been closed to the use of third-party cookies as a result of legal restrictions. The name “sandbox” seems to have been given to this technology, which is currently under development.
All of this can only be described as EVIL. Even if your site is being served by Google’s ads, you should disable them.
There is a way to opt out through browser settings, but they no longer make any attempt to hide their intent to deceive those with little concern for security or privacy. Some features can be disabled (at least for this site) via server-side response headers, so let’s disable them for the good of all humankinnd.
Topics API
Permissions-Policy: browsing-topics=()
The Topics API is a means of tracking users across sites, replacing third-party cookies. Google got flammed worldwidely when it announced FLoC (Federated Learning of Cohorts), a system for grouping users by preferences, and the Topics API seems to be the successor of FLoC. It allows browsers to list and store categories of interest based on the user’s browsing history, and allows the top five categories to be browsed upon request from the website side. However, there is a 5% chance that a random category will be returned.
You can disable the viewer’s browsers to analize contents
(at least on your site) by setting
browsing-topics=()
2.
FLoC
Permissions-Policy: interest-cohort=()
interest-cohort
is FLoC as explained above. It
has been succeeded to the Topics API yet you can opt it out
with interest-cohort=()
just in case.
Protected Audience API
Permissions-Policy: join-ad-interest-group=(), run-ad-auction=()
The Protected Audience API is a system that divides users with common interests into the Interest Groups in advance and run auctions for advertising on their devices. It was originally developed under the name FLEDGE (First “Locally-Executed Decision over Groups” Experiment), but it was renamed “Protected Audience” in 20233.
The target audience is “users who have visited a company’s website in the past,” and the purpose is to give the company an opportunity to remarket to these users.
You can disable adding browsers to interest groups with
join-ad-interest-group=()
. You can disable the
ability to run auctions with run-ad-auction=()
,
but this may not be relevant if you are not loading ad
scripts. However, it was not clear to me what data is used to
add the ads to the interest group. (This seems to be similar
to the Topics API, but unclear.)
Attribution Reporting API
Permissions-Policy: attribution-reporting=()
The Attribution Reporting API is a system that allows browsers to send the results of measurement of “when an ad view led to a purchase” to the advertising platform later. It seems completely irrelevant to sites that do not use any ads, such as this site. Although Google is implying that they are going to use for purposes other than advertising in the future4, so it may be a good idea to disable it from now5.
Referrer-Policy
Referrer-Policy: strict-origin-when-cross-origin
Restrict the referer sent in the request.
Basically, at least between the same origin, sending full URL referer makes it easy to inspect logs, and it is acceptable to send only the domain name to external origins. However, you do not want to send anything from an HTTPS origin to an HTTP origin at all.
In such a case, setting no-referrer
is not
proper because it makes it difficult to inspect requests even
between the same origin. The best choice is
strict-origin-when-cross-origin
. This is the
default setting for almost all browsers, including Chrome and
Firefox.
Reducing network traffic
Cache-Control
Cache-Control: max-age=15768000
Browsers do not send all HTTP requests, but may create
responses from caches stored in themselves. By using
max-age
to inform browsers how long the response
can be reused, it is possible to reduce the extra traffic load
on the client.
There is no need to worry that users will not be notified
of site updates even if max-age
is set to a
longer value. The browser judges whether the response body it
receives is the same as the one in its cache based on the
values of last-modified
and etag
in
the response header, and returns the status code
304 Not Modified
if they are the same, and
creates a response body from its cache. If so, it returns the
status code 304 Not Modified
and creates a
response body from the cache. If you find many
304
s in the server logs, you should set a longer
cache period.
In addition, modern browsers are smart enough to think that
a site that has not been updated in a year is unlikely to be
updated again, so they sometimes reuse caches that are more
than a year old from last-modified
and have long
passed their max-age
. This is called a “heuristic
cache”.
Troubleshooting
Now, I have introduced a fairly strict configuration method, but strict settings sometimes impair convenience.
I would like to show some actual problems caused by HTTP header settings.
SVG turns to be a black
This is the favicon of this site. You’ve seen it right?
One day a while after setting CSP. I suddenly wondered, “Is the favicon displayed properly?” and I opened them in my browser.
All of them were displayed correctly but only
favicon.svg
was displayed as a black square.
At first I thought that the dark mode of the browser might be doing something wrong, but there was no probrem with that. I finally found the cause by downloading and opening the file with a text editor.
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
svg
< version="1.1"
id="svg1"
width="256.00003"
height="256.00003"
viewBox="0 0 256.00003 256.00003"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
style id="style1">
<
path {
fill: #000;
}
@media (prefers-color-scheme: dark) {
path {
fill: #fff;
}
}style>
</defs id="defs1" />
<rect
< style=""
id="rect1"
width="256"
height="256"
x="0"
y="0" />
path
< style=""
d="M 0,127.99999 (...) "
id="path3" />
svg> </
The <rect>
and <path>
styles are completely missing. (I wonder why style1 is still
there. It doesn’t change color even in dark mode, so I
actually don’t need it.)
I solved the problem by returning
Content-Security-Policy: style-src 'unsafe-inline';
only for requests for image/svg+xml
.
base64 image does not show up
One more thing related to CSP. There’s a base64-encoded
image on some page on this site, but it was not shown with the
CSP set to default-src: 'self'
. To display it, I
had to specify the scheme by adding
img-src 'self' data:;
Twitter OGP
In this site, illustration pages are set up with metadata for OGP (Open Graph Protocol), to share on SNS (especially Twitter). It makes possible to “embedding the webpage in Twitter”.

Example of OGP
However, for this site, there was a problem that only the thumbnail images were not loaded even though the metadata of the articles were loaded.

The official OGP Validator was ended, so checking at the post page.
The thumbnails on this site are 160x160, which is much
smaller than the usual OGP images (often 1200x630), so I
thought that it might be running afoul of the minimum size,
but could not solve it6. I left it for
a while because it was troublesome, but then I noticed that it
could be a CSP problem, and tried setting
Cross-Origin-Resource-Policy: cross-origin
to the
directory for thumbnails, and they were displayed.

Thumbnail is shown properly
Websites I reffered
Specific sources are noted in footnotes.
HTTP header - MDN Web Docs Glossary: Definitions of Web-related terms | MDN
閲覧履歴を元に興味・関心をブラウザが推測して広告主と共有する機能など、プライバシーサンドボックスに含まれるAPIを段階的に有効化していくとGoogleが発表 - GIGAZINE
RFC 6648 - Deprecating the “X-” Prefix and Similar Constructs in Application Protocols↩︎
You can opt out of topic calculation for specific pages on your site by including the
Permissions-Policy: browsing-topics=()
Permissions-Policy header on a page to prevent topics calculation for all users on that page only. Subsequent visits to other pages on your site won’t be affected: if you set a policy to block the Topics API on one page, this won’t affect other pages.Note: In the future, the Attribution Reporting API may serve use cases that are not related to advertising.
– Attribution Reporting for Web overview | Privacy Sandbox↩︎
A site can disable the Attribution Reporting API for all parties, including scripts with top-level access, by sending the HTTP response header:
Permissions-Policy: attribution-reporting=()
I couldn’t find any limitations on the size of OGP image on the Twitter’s document. | Cards markup | Docs | Twitter Developer Platform↩︎