You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[BUG]: Access blocked for OpenStreetMap tiles #5562
Generating a map from OpenStreetMap, some tiles are filled with the error message: "403r Access blocked. Referer is required by tile usage policý of OpenStreetMap's volunteer-run servers: osm.wiki/Blocked"
This occurs randomly about half the times, on one or more tiles.
Screenshots/Video
Steps to reproduce
Python code:
import plotly.graph_objects as go
lat = 41.580472
lon = -1.1161953
fig = go.Figure(go.Scattermap(
lat=[lat],
lon=[lon],
mode='markers',
marker=dict(symbol="circle", size=30)
))
fig.update_layout(
map=dict(
style="open-street-map",
center=dict(lat=lat, lon=lon),
zoom=16
),
margin={"r": 0, "t": 0, "l": 0, "b": 0}
)
img = fig.to_image('png')
with open('map.png', 'wb') as f:
f.write(img)
According to OSM's tile policy https://operations.osmfoundation.org/policies/tiles/ you have to either set "a valid HTTP User-Agent [header]" or "a valid HTTP Referer header", otherwise you might get throttled as above.
Plotly should allow to provide either of these so OSM people are happy.
I just tried running your example and didn't see an issue. Is the problem still happening for you?
For us the issue happens every 50% of times a map is rendered, I'd say. Only some of the tiles in view would be replaced by the error. Perhaps it's a usage thing that regular testing wouldn't bring up?
Either way, I took a look at the obfuscated frontend code, and it seems like the referrer is passed along when creating the Request object, but the browser ignores the argument and sets it to about:client. At least, that's the case for us when rendering it in a new iframe (using Viktor). Why this is the case I do not know, but that's why OpenStreetMap is saying the referer header is missing.
Okay, I was able to reproduce it. That's clearly an issue somewhere in the rendering chain. Our team won't be able to dig into this any time soon, but we can prioritize a PR review if you want to investigate and fix the problem.
I took a look at the source code, but I couldn't find where do you either have a function that generates that image, or call a function that does it. @elpamplina said that at some point there's a browser involved?
I have found that the JS core does try to get the referrer, but the worker created by the Python wrapper cannot give it to it (probably because being a background instance created by the wrapper it has no real reference).
You can find the referrer in the line 176742 in the unobfuscated core library: https://github.com/plotly/plotly.js/blob/a6663c92620c222b0fd9b01495c724bbde4ab5d3/dist/plotly.js
We would have to look for the reason why some tiles are loaded and others not because different OSM servers will have different referrer policies, and it randomly coincides that at some point they all accept it.
I think the best solution is for the wrapper to support a referrer attribute, so that it can be passed to the core JS and thus comply with the policy.
For instance:
We would have to look for the reason why some tiles are loaded and others not because different OSM servers will have different referrer policies, and it randomly coincides that at some point they all accept it.
It's something that the OSM tile server does, it only replaces some of the tiles with the one with the error, so users can find the issue by themselves and act accordingly without flatly refusing to serve tiles.
The calls to the tile server go through the maplibre library, which is where that code reference comes from in the bundle. That library does try to add the referrer properly, but something is breaking between that point and the rendering of the image. The image rendering is done with Kaleido, which uses a headless version of Chrome. It's possible the headers are being lost during that process.
I'm working on a fix. The change needs to be made in both Kaleido and plotly.py. Choreographer communicates with headless Chrome and the instructions for that come from Kaleido. Kaleido isn't set up yet to pass in headers, so that work needs to be done. Then plotly.py needs to be updated to allow for passing in headers. We'll add a default header for Referrer, but users can update that if they want. This is all a plan and could change, but I ran some tests locally and the tiles stopped showing the issue.
I'm working on a fix. The change needs to be made in both Kaleido and plotly.py. Choreographer communicates with headless Chrome and the instructions for that come from Kaleido. Kaleido isn't set up yet to pass in headers, so that work needs to be done. Then plotly.py needs to be updated to allow for passing in headers. We'll add a default header for Referrer, but users can update that if they want. This is all a plan and could change, but I ran some tests locally and the tiles stopped showing the issue.
Many many thanks for the effort. Greatly appreciated. ☺️
For reference, the header that needs to be added is "Referer" for web apps or "X-Requested-With" for non-web apps (source). Since Kaleido runs Choreographer under the hood and that uses headless Chrome, no "Referer" header is being included in requests, so we'll (likely) be adding "X-Requested-With" for requests originating from plotly.py.
Activity
StyXman commentedon Apr 7, 2026
According to OSM's tile policy https://operations.osmfoundation.org/policies/tiles/ you have to either set "a valid HTTP User-Agent [header]" or "a valid HTTP Referer header", otherwise you might get throttled as above.
Plotly should allow to provide either of these so OSM people are happy.
camdecoster commentedon Apr 8, 2026
I just tried running your example and didn't see an issue. Is the problem still happening for you?
camdecoster commentedon Apr 8, 2026
The image I produced:
NorthLake commentedon Apr 8, 2026
For us the issue happens every 50% of times a map is rendered, I'd say. Only some of the tiles in view would be replaced by the error. Perhaps it's a usage thing that regular testing wouldn't bring up?
Either way, I took a look at the obfuscated frontend code, and it seems like the referrer is passed along when creating the Request object, but the browser ignores the argument and sets it to
about:client. At least, that's the case for us when rendering it in a new iframe (using Viktor). Why this is the case I do not know, but that's why OpenStreetMap is saying the referer header is missing.elpamplina commentedon Apr 9, 2026
The problem occurs randomly, about once in two.
camdecoster commentedon Apr 9, 2026
Okay, I was able to reproduce it. That's clearly an issue somewhere in the rendering chain. Our team won't be able to dig into this any time soon, but we can prioritize a PR review if you want to investigate and fix the problem.
StyXman commentedon Apr 9, 2026
I took a look at the source code, but I couldn't find where do you either have a function that generates that image, or call a function that does it. @elpamplina said that at some point there's a browser involved?
elpamplina commentedon Apr 10, 2026
I have found that the JS core does try to get the referrer, but the worker created by the Python wrapper cannot give it to it (probably because being a background instance created by the wrapper it has no real reference).
You can find the referrer in the line 176742 in the unobfuscated core library:
https://github.com/plotly/plotly.js/blob/a6663c92620c222b0fd9b01495c724bbde4ab5d3/dist/plotly.js
We would have to look for the reason why some tiles are loaded and others not because different OSM servers will have different referrer policies, and it randomly coincides that at some point they all accept it.
I think the best solution is for the wrapper to support a referrer attribute, so that it can be passed to the core JS and thus comply with the policy.
For instance:
I do not have the precise knowledge of how wrapper works to do it myself.
StyXman commentedon Apr 10, 2026
It's something that the OSM tile server does, it only replaces some of the tiles with the one with the error, so users can find the issue by themselves and act accordingly without flatly refusing to serve tiles.
Fix plotly#5562: Update blocked OpenStreetMap tile server URL
camdecoster commentedon Apr 10, 2026
The calls to the tile server go through the maplibre library, which is where that code reference comes from in the bundle. That library does try to add the referrer properly, but something is breaking between that point and the rendering of the image. The image rendering is done with Kaleido, which uses a headless version of Chrome. It's possible the headers are being lost during that process.
JoGorska commentedon Apr 14, 2026
I was wondering if this could be connected with openmaptiles returning phishing error
AliaHa3 commentedon Apr 20, 2026
any solution?
camdecoster commentedon Apr 22, 2026
I'm working on a fix. The change needs to be made in both Kaleido and plotly.py. Choreographer communicates with headless Chrome and the instructions for that come from Kaleido. Kaleido isn't set up yet to pass in headers, so that work needs to be done. Then plotly.py needs to be updated to allow for passing in headers. We'll add a default header for
Referrer, but users can update that if they want. This is all a plan and could change, but I ran some tests locally and the tiles stopped showing the issue.elpamplina commentedon Apr 23, 2026
Many many thanks for the effort. Greatly appreciated.☺️
camdecoster commentedon Apr 23, 2026
For reference, the header that needs to be added is "Referer" for web apps or "X-Requested-With" for non-web apps (source). Since Kaleido runs Choreographer under the hood and that uses headless Chrome, no "Referer" header is being included in requests, so we'll (likely) be adding "X-Requested-With" for requests originating from plotly.py.
camdecoster commentedon May 5, 2026
Is anyone able to reproduce this today? I'm having trouble triggering the issue.
elpamplina commentedon May 6, 2026
There're no issues in my logs since 2026-04-30. It seems something changed last week. New OSM policies? 🤔
camdecoster commentedon May 6, 2026
Perhaps. I'll still move forward with the change, which did fix the problem when I could still get it to happen. Thanks for checking.
elpamplina commentedon May 15, 2026
Many thanks, Cameron!