Skip to content

Instantly share code, notes, and snippets.

@mary-ext
Last active August 25, 2025 13:17
Show Gist options
  • Save mary-ext/6e27b24a83838202908808ad528b3318 to your computer and use it in GitHub Desktop.
Save mary-ext/6e27b24a83838202908808ad528b3318 to your computer and use it in GitHub Desktop.
Bluesky's age assurance sucks, here's how to work around it.

Bluesky's age assurance sucks, here's how to work around it.

Bluesky has implemented age verification measures in response to regional laws that restrict access:

  • UK users are shown a banner for attempting identity verification through Epic Games' Kids Web Services before they could access adult content, following the Online Safety Act.

  • Mississippi users are completely blocked from acccessing Bluesky due to HB1126, which would require identity verification for all users regardless of content preferences.

This sucks, but thankfully there are ways to work around it.

Workaround methods

Method 0. VPN

This was obvious, but using a VPN to connect from outside your location will bypass the age assurance requirements entirely.

If you don't already have one, a VPN routes your connection through servers in other countries and prevents your ISP from tracking and selling your browsing data, though you're essentially trading trust from your ISP to your VPN provider instead.

My personal recommendations are Mullvad, IVPN or ProtonVPN.

Don't fall for VPN services that heavily focus on marketing, especially if they market features like "military-grade encryption" or make exaggerated privacy claims.

Method 1. Third-party clients

You don't have to use the official Bluesky app to access the platform. Third-party clients like Klearsky or TOKIMEKI currently don't implement these restrictions. For read-only access without signing in, Anartia is also available.

Note that these alternatives work only as long as their developers don't implement similar age assurance measures or block UK users outright (and as the person behind Anartia, I have no plans to implement such thing.)

Method 2. Custom adblock filter rules

You can add the following filter rules to your adblocker.

||bsky.app/ipcc$replace=/(?<="countryCode":").+?(?=")/US/
||bsky.app/ipcc$replace=/(?<="isAgeRestrictedGeo":)true/false/
||bsky.app/ipcc$replace=/(?<="isAgeBlockedGeo":)true/false/

||ip.bsky.app/config$replace=/(?<="countryCode":").+?(?=")/US/
||ip.bsky.app/config$replace=/(?<="regionCode":").+?(?=")/WA/
||ip.bsky.app/config$replace=/(?<="isAgeRestrictedGeo":)true/false/
||ip.bsky.app/config$replace=/(?<="isAgeBlockedGeo":)true/false/

for uBlock Origin (Firefox)

  1. Open uBlock Origin dashboard › My filters
  2. Enable Enable my custom filters
  3. Enable Allow custom filters requiring trust
  4. Add the rules provided above

for Brave browser (Desktop, Android)

  1. Open Settings › Shields › Content filtering
  2. Enable Developer mode
  3. Add the rules provided above to Create custom filters

for Ghostery (Firefox)

  1. Open Ghostery extension window › Ad-Blocking › Custom Filters
  2. Make sure it's enabled
  3. Add the rules provided above

for Adguard (Firefox)

  1. Open Adguard Settings › User Rules
  2. Make sure it's enabled
  3. Add the rules provided above

for Adguard (Android)

Note: Custom filter rules is a paid feature on the Android version.

Based on testing, this seems to produce wonky results. The setup process is also more complex. But if it works, it allows you to use Bluesky's Android app as usual.

  1. Create a text file containing the filter rules provided above and save it to somewhere accessible
  2. Open Adguard and navigate to Settings › Filtering › Filters › Custom Filters › Add custom filter
  3. Tap Browse and locate the text file you created
  4. Select the file to add it as a custom filter list

Method 3. Custom DNR rules (Chrome, Safari)

Chrome's Manifest v3 changes broke all adblockers including uBlock Origin by replacing the webRequest API that they rely on with the less powerful declarativeNetRequest API instead. uBlock Origin Lite was created specifically to work with DNR, and thankfully it's still very usable for our purposes.

  1. Install uBlock Origin Lite
  2. Open uBlock Origin Lite dashboard
  3. Enable Developer mode
  4. Head to Develop › View: Custom DNR rules
  5. Insert the following rules:
---
priority: 1
action:
  type: redirect
  redirect:
    url: https://gist.githubusercontent.com/mary-ext/6e27b24a83838202908808ad528b3318/raw/ipcc-response.json
condition:
  requestDomains:
    - bsky.app
  initiatorDomains:
    - bsky.app
    - main.bsky.dev
  urlFilter: /ipcc
  resourceTypes:
    - xmlhttprequest
---
priority: 1
action:
  type: redirect
  redirect:
    url: https://gist.githubusercontent.com/mary-ext/6e27b24a83838202908808ad528b3318/raw/ipcc-response.json
condition:
  requestDomains:
    - ip.bsky.app
  initiatorDomains:
    - bsky.app
    - main.bsky.dev
  urlFilter: /config
  resourceTypes:
    - xmlhttprequest
---
priority: 1
action:
  type: redirect
  redirect:
    url: https://gist.githubusercontent.com/mary-ext/6e27b24a83838202908808ad528b3318/raw/getAgeAssuranceState-response.json
condition:
  initiatorDomains:
    - bsky.app
    - main.bsky.dev
  urlFilter: /xrpc/app.bsky.unspecced.getAgeAssuranceState
  resourceTypes:
    - xmlhttprequest
---

Method 4. Userscripts and Brave scriptlets

You can use userscripts, or scriptlets in Brave.

for userscript extensions

  1. Install a userscript manager

  2. Install this userscript

for Brave browser (Desktop)

Open Settings › Shields › Content filtering (or go to about:adblock)

Enable Developer mode, and add the following scriptlet, saving it as user-bsky-age-assurance.js.

const _fetch = globalThis.fetch;
globalThis.fetch = async function (req, init) {
	if (req instanceof Request) {
		const url = new URL(req.url);

		switch (url.pathname) {
			case '/xrpc/app.bsky.unspecced.getAgeAssuranceState': {
				return Response.json({
					lastInitiatedAt: '2025-07-14T14:22:43.912Z',
					status: 'assured',
				});
			}
		}
	} else if (req === 'https://bsky.app/ipcc' || req === 'https://ip.bsky.app/config') {
		return Response.json({
			countryCode: 'US',
			regionCode: 'WA',
			isAgeBlockedGeo: false,
			isAgeRestrictedGeo: false,
		});
	}

	return _fetch.call(this, req, init);
};

Then reference the scriptlet in a custom filter.

bsky.app##+js(user-bsky-age-assurance.js)
main.bsky.dev##+js(user-bsky-age-assurance.js)

Method 5. Self-hosted PDS (for UK users)

If your account is hosted on a PDS you own or control, you can add these rules to your Nginx or Caddy configuration.

for Nginx users:

server {
	server_name pds.example.com;

	location /xrpc/app.bsky.unspecced.getAgeAssuranceState {
		default_type application/json;
		add_header access-control-allow-headers "authorization,dpop,atproto-accept-labelers,atproto-proxy" always;
		add_header access-control-allow-origin "*" always;
		return 200 '{"lastInitiatedAt":"2025-07-14T14:22:43.912Z","status":"assured"}';
	}
}

for Caddy users:

pds.example.com {
	handle /xrpc/app.bsky.unspecced.getAgeAssuranceState {
		header content-type "application/json"
		header access-control-allow-headers "authorization,dpop,atproto-accept-labelers,atproto-proxy"
		header access-control-allow-origin "*"
		respond `{"lastInitiatedAt":"2025-07-14T14:22:43.912Z","status":"assured"}` 200
	}
}

Background

Bluesky's implementation of age-based content restrictions is entirely client-side, this is an intentional design decision. The API (called the "AppView") doesn't impose content restrictions directly and isn't aware of your location as requests are proxied through your account's hosting server (called the "PDS").

bsky.app (or the "client") checks your location by making a request to https://bsky.app/ipcc, which returns:

  • countryCode: your country based on IP address, which is used for regional content moderation.
  • isAgeRestrictedGeo: whether your location mandates identity verification
  • isAgeBlockedGeo: whether your location is blocked from accessing Bluesky

When isAgeRestrictedGeo is true, it will then make a request to <pds host>/xrpc/app.bsky.unspecced.getAgeAssuranceState, which returns:

  • status: your account's current verification status
  • lastInitiated: when you last started the identity verification process

These workarounds exploit the client-side nature of these checks.

Worth noting that decentralized social networks aren't above the law. All platforms, including Mastodon and other federated networks, must comply with local regulations like the Online Safety Act, and a fully decentralized network may need individual server operators to handle compliance.

It remains unclear whether regulators would consider Bluesky's client-side implementation sufficient for legal compliance. However, Bluesky Social PBC could reasonably argue the restrictions are effectively enforced for users on their PDS using unmodified clients, and that's as far as they can guarantee in a decentralized network like Bluesky.

I am not a lawyer however.

What you can do next

While these workarounds provide immediate relief, consider taking action to address the root cause:

Contact your representatives

  • UK users: Reach out to your Member of Parliament to express concerns about the Act's impact on privacy and digital rights. You can find your MP and their contact details on the UK Parliament website.

  • Mississippi users: Contact your state legislators about HB1126's impact on free speech and access to information. You can find your representatives on the Mississippi Legislature website.

Support digital rights organizations

Consider supporting or getting involved with organizations that advocate for digital rights and privacy:

International:

UK-specific:

US-specific:

// ==UserScript==
// @name Bluesky age assurance bypass
// @version 1.0
// @match https://bsky.app/*
// @match https://main.bsky.dev/*
// @grant none
// @run-at document-start
// ==/UserScript==
const _fetch = globalThis.fetch;
globalThis.fetch = async function (req, init) {
if (req instanceof Request) {
const url = new URL(req.url);
switch (url.pathname) {
case '/xrpc/app.bsky.unspecced.getAgeAssuranceState': {
return Response.json({
lastInitiatedAt: '2025-07-14T14:22:43.912Z',
status: 'assured',
});
}
}
} else if (req === 'https://bsky.app/ipcc' || req === 'https://ip.bsky.app/config') {
return Response.json({
countryCode: 'US',
regionCode: 'WA',
isAgeBlockedGeo: false,
isAgeRestrictedGeo: false,
});
}
return _fetch.call(this, req, init);
};
{
"lastInitiatedAt": "2025-07-14T14:22:43.912Z",
"status": "assured"
}
{
"countryCode": "US",
"regionCode": "WA",
"isAgeBlockedGeo": false,
"isAgeRestrictedGeo": false
}
@kindgracekind
Copy link

Mention VPN as an option?

@mary-ext
Copy link
Author

mary-ext commented Jul 24, 2025

I mean I could...

edit: added

@dangerousdriver
Copy link

Not working for me (uBlock Origin method) in either Chrome or FF.

image

@mary-ext
Copy link
Author

mary-ext commented Jul 24, 2025

What shows up if you visit https://bsky.app/ipcc? Make sure you reload Bluesky after adding the filter rules.

@dangerousdriver
Copy link

{"countryCode":"GB","isAgeRestrictedGeo":true}

@mary-ext
Copy link
Author

mary-ext commented Jul 24, 2025

Hrmm, you're using uBlock Origin on Chrome? I wonder if it's because it's not supported there, I don't have an old copy of Chrome laying around so I can't test this for you sorry.

I am not sure why it isn't working for you on Firefox though, could you close all the tabs, and then restart the browser?

@dangerousdriver
Copy link

Must be another extension in Chrome - got working in FF with only uBlock active.

@tulpenkiste
Copy link

tulpenkiste commented Jul 24, 2025

Theres also this extension which can be used for bypassing. Works well for me on Zen (firefox)

@stucka
Copy link

stucka commented Jul 24, 2025

Yesterday I launched Chrome for the first time in ages and discovered uBlock Origin no longer works, but the same person makes uBlock Origin Lite; I don't know if that allows for the same kind of filtering.

@kiri-thornalley
Copy link

kiri-thornalley commented Jul 24, 2025

Can confirm userscript works with Tampermonkey on Chrome and Opera - just wait for it to load first.

@mary-ext
Copy link
Author

uBO Lite is very capable, but no you can't set custom filter lists or rules, it's all prebaked.

@cpressland
Copy link

I'm using Policy Based Routes on my UniFi device to route Bluesky, Discord, etc via Mullvad, leaving the rest of my internet traffic intact. The following domains seem to be the main ones to route: bsky.app, cdn.bsky.app, web-cdn.bsky.app

@mary-ext
Copy link
Author

you only need to route bsky.app in particular

@pigsonthewing
Copy link

Excellent, thank you.

Any tips for users of Blokada?

Also, for those of us watching here, please post a note if you have tips for other services, like Reddit.

@mary-ext
Copy link
Author

Blokada doesn't do request transformations, it can only perform blocking/redirection at a domain level

@mary-ext
Copy link
Author

mary-ext commented Aug 23, 2025

uBO Lite is very capable, but no you can't set custom filter lists or rules, it's all prebaked.

I've tacked on a section for uBO Lite because it seems that the extension allows you to add custom DNR rules (not the same as ABP/uBlock/Adguard rules unfortunately but it works for our purposes here)

@mary-ext
Copy link
Author

Updated again since Bluesky changed the IPCC endpoint from https://bsky.app/ipcc to https://ip.bsky.app/config

@ThatOneGitWhoPostsStuff

Does anyone know if someone has made a similar version for other websites that have been effected by these stupid laws, like rule34.xxx?

@mary-ext
Copy link
Author

not unless they're implemented the same way as Bluesky did it, honestly you're better off just using a VPN

@tulpenkiste
Copy link

Does anyone know if someone has made a similar version for other websites that have been effected by these stupid laws, like rule34.xxx?

Your example doesn't need one, they only implement the check on the frontend so you can use a client to bypass the OSA prompt

@ThatOneGitWhoPostsStuff

Does anyone know if someone has made a similar version for other websites that have been effected by these stupid laws, like rule34.xxx?

Your example doesn't need one, they only implement the check on the frontend so you can use a client to bypass the OSA prompt
Could you elaborate on that? I don't think I'm tech literate to understand what you're saying.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment