For Traditional Chinese documentation, see README.zh-TW.md.
License: CC BY-NC-ND 4.0 International (see LICENSE)
Important online services that should keep operating as normally as possible when Taiwan loses external connectivity due to natural disasters or human-caused incidents.
Scenario: In early 2023, outbound submarine cables to the Matsu islands were accidentally cut by Chinese trawlers/dredgers, leaving the islands offline for months. Suppose the same scenario happened in Taiwan: Taiwan’s outbound backbone submarine cables are cut by 80–90% (or completely). Which services are necessary to maintain a basic quality of life and should keep operating normally when only in-island connectivity remains?
Scope: “Online services” and “mobile apps” that citizens use on the front line.
Physical offline services (e.g. 7-Eleven stores, MRT, etc.) are out of scope. Upstream infrastructure for consumer-facing services (e.g. e-commerce logistics) is also excluded—we expect each site to coordinate with its supply chain on further resilience plans.
Goal: Test the listed essential civilian services and confirm their resilience when Taiwan loses external connectivity.
Advocacy: Services essential to daily life should have contingency plans for severe outbound connectivity failures and should run related drills regularly.
-> See: Will the site still work when submarine cables are cut?
The community collectively lists digitally important civilian services and related infrastructure.
-> Critical digital civilian services (and alternatives) (Chinese source: critical-digital-services.zh-TW.md; also on HackMD)
- Website hosting
- Location & API location
- Location: domestic / abroad
- Whether it uses anycast with domestic nodes
- Location & API location
- Whether pages & APIs go through a CDN
- Whether the CDN is a known provider with local presence
- e.g. Cloudflare (TPE), Akamai
- Whether the CDN is a known provider with local presence
- Libraries used by the site (jQuery, Angular, Vue, etc.)
- Whether public CDNs are used
- Whether local presence is known
- cdnjs (over Cloudflare)
- jsdelivr?
- Whether assets are served together with the site
- Whether public CDNs are used
Using a PChome product page https://24h.pchome.com.tw/prod/DCAYAD-A900BIAMV as an example (test process notes)
-
Enable Adblock / AdGuard first to block unnecessary elements upfront
-
Open browser devtools, disable cache, load the page
-
Switch to Network, save a full request log as a HAR file
-
Data cleanup
- In VS Code, search the HAR for
"url": "(.*)"to extract all requests - Sort by hostname; keep only one entry per subdomain
-
Requests you can drop directly
Refer to your ad-blocker results (e.g. if uBlock blocked it, you can discard it)
- analytics:
analytics.google.complay.google.com/logwww.google-analytics.com
- fb:
connect.facebook.netwww.facebook.com
- fonts:
fonts.gstatic.com
- ad:
*.doubleclick.netwww.google.com.tw/ads*.scupio.comjscdn.appier.net
- other:
www.youtube.com/embed/*
- analytics:
- In VS Code, search the HAR for
-
Review each request under HAR entries for domestic availability
Using the first entry
https://24h.pchome.com.tw/prod/DCAYAD-A900BIAMVas an examplea. Confirm resource information
➜ ~ ipinfo 24h.pchome.com.tw Core - IP 34.149.253.14 - Anycast true - Hostname 14.253.149.34.bc.googleusercontent.com - City Kansas City - Region Missouri - Country United States (US) - Currency USD ($) - Location 39.0997,-94.5786 - Organization AS396982 Google LLC - Postal 64106 - Timezone America/Chicagob. Review anycast / geographic status
b-1. If there is no anycast, use the IP’s geography and record it in the table. If the location is domestic, mark O under “reachable”; if abroad, mark X.
b-2. If there is anycast and the geo is not domestic, check whether the service is a known provider with Taiwan nodes. In the example above the hostname is on GCP; compare with Cloud platforms — IaaS and if Taiwan nodes exist, record - under “reachable”.
c. Finally, score tolerance from counts of X and -. For the PChome product page example: 7 O (domestic), 10 - (cloud, may tolerate outage), 0 X (non-cloud foreign nodes).
https://github.com/irvin/digital-service-resilience
git clone https://github.com/irvin/digital-service-resilience.git
cd digital-service-resilience
npm installexport IPINFO_TOKEN=your_token_here # Linux/Mac
set IPINFO_TOKEN=your_token_here # Windows CMD
$env:IPINFO_TOKEN="your_token_here" # Windows PowerShellnpm run check https://example.com
# or
node no-global-connection-check.js https://example.comCustom DNS server
node no-global-connection-check.js --dns 8.8.8.8 https://example.comSave test results
node no-global-connection-check.js --save https://example.comSpecify IPinfo token
node no-global-connection-check.js --ipinfo-token your_token https://example.comAdblock list options
The tool loads EasyList and EasyPrivacy by default to filter ad and tracking domains.
- Use default adblock lists (default):
node no-global-connection-check.js https://example.com- Disable adblock lists:
node no-global-connection-check.js --adblock false https://example.com- Custom adblock list:
node no-global-connection-check.js --adblock-url https://filter.futa.gg/hosts_abp.txt https://example.com- Multiple custom lists (comma-separated):
node no-global-connection-check.js --adblock-url https://filter.futa.gg/hosts_abp.txt,https://filter.futa.gg/nofarm_abp.txt https://example.com- Debug mode (verbose output):
node no-global-connection-check.js --debug https://example.comDebug mode shows:
-
All collected requests
-
Cleaned domain list
-
Ignored domains
-
IP lookup per domain
-
Adblock list load info
-
Stack traces on errors
-
Disable cache (force re-download of adblock lists and IPinfo data):
node no-global-connection-check.js --cache false https://example.com- Headless mode:
# Default: non-headless (visible browser, closer to real users)
node no-global-connection-check.js https://example.com
# Headless (no browser window; use on CI or headless servers)
node no-global-connection-check.js --headless true https://example.comAdvantage: No visible browser window, lower resource use, and page loads are often faster per site—useful for large batch runs. If headless fails, the retry flow still falls back to non-headless automatically.
Trade-off: Use headless when batch speed matters; keep the default non-headless if you want page loads to behave more like a typical user opening a browser.
This tool uses CDN response headers (e.g. Azure x-azure-ref, Microsoft x-msedge-ref) to infer Taiwan PoP. Playwright headless and headed modes differ from a typical desktop browser in User-Agent, Sec-CH-UA, TLS fingerprints, and related signals; we have not systematically verified whether those differences change CDN routing or PoP headers. During development we observed that PoP-related headers for the same site can vary across clients or repeated connections.
The default is non-headless as a design choice to stay closer to how users normally open a browser. For faster large batch runs, pass --headless true (failures still fall back to non-headless via the retry flow).
Note: On failure the tool retries as follows (default non-headless):
- Normal URL (non-headless)
wwwprefix (non-headless)
With --headless true, retries are:
- Normal URL (headless)
- Normal URL (non-headless)
wwwprefix (headless)wwwprefix (non-headless)
Use batch-test.js to test many sites. The list must be JSON with website, url, and rank fields.
node batch-test.js --limit 10 top-traffic-list-taiwan/merged_lists_tw.jsonThe test list file path must be the last CLI argument.
- Limit count:
node batch-test.js --limit 50 top-traffic-list-taiwan/merged_lists_tw.json- Start from offset:
node batch-test.js --limit 50 --start-from 10 top-traffic-list-taiwan/merged_lists_tw.json- Request delay (milliseconds):
node batch-test.js --delay 3000 --limit 10 top-traffic-list-taiwan/merged_lists_tw.json- Combine options (supports all single-site flags):
node batch-test.js --debug --adblock-url https://filter.futa.gg/hosts_abp.txt --adblock false --cache false --limit 10 --delay 2000 top-traffic-list-taiwan/merged_lists_tw.jsonBatch-supported flags (same as single-site):
--adblock true/false: use adblock lists (default: true)--cache true/false: use cache (default: true)--headless true/false: headless mode (default: non-headless;trueis often faster, for CI/large batches)--adblock-url URL: custom adblock list URL--dns IP: custom DNS--ipinfo-token TOKEN: IPinfo API token--debug: debug mode--timeout N: page load timeout (seconds)
Batch testing will:
- Write per-site results under
test-results/ - Write a summary
batch_summary_<timestamp>.jsonat the repo root with:- Test parameters
- Stats (total, success, failure, skipped)
- Result summaries
[
{
"website": "example.com",
"url": "https://example.com",
"rank": 1
},
{
"website": "another.com",
"url": "https://another.com",
"rank": 2
}
]- O: service hosted in Taiwan
- ?: cloud with known Taiwan nodes (e.g. Google Cloud, AWS)
- X: abroad and not a tolerant cloud setup
To test a new site and add it to the results corpus:
npm run check --save https://www.example.comNotes:
--savewrites JSON undertest-results/, roughly{hostname+path}.json- e.g.
https://www.article19.org→test-results/www.article19.org.json
Optional flags:
--debug: verbose process output--adblock false: skip adblock filtering--timeout N: page load timeout in seconds (default 120)--headless true: headless mode (headless servers; often faster)
For many sites at once, prefer
batch-test.js; it callscheckWebsiteResilience(... --save)per site and runs statistics at the end.
node generate_statistic.jsNotes:
- Reads all JSON under
test-results/ - Creates/updates
test-results/statistic.tsv - Sort order follows
top-traffic-list-taiwan/merged_lists_tw.json; sites not in the list are appended
To analyze Taiwan-node usage for international clouds (Google, Cloudflare, AWS, Akamai, Fastly, Microsoft):
node asn_taiwan_ratio.jsThis reads test-results/*.json and writes test-results/asn_taiwan_ratio.tsv with:
- Per-ASN/company request stats (
Total Requests,Taiwan Requests,Non-Taiwan Requests,Taiwan Ratio (%)) - Per-company site counts (
Websites (domestic node),Websites (foreign node)) - Among
resilience=1sites (no foreign dependency), count using “public cloud Taiwan nodes”
For “international public cloud dependency” figures in the report, treat
asn_taiwan_ratio.tsvas the source of truth and refresh regularly.
To build publishable HTML from report/index.md and report/en.md, compile decks under report/slide/, or publish to the report branch, see report/README.md (Traditional Chinese: report/README.zh-TW.md). Published URLs: /web/report/ (zh-TW), /web/report/en.html (English), and /web/report/slide/ for slides.
Chart output (report/img) and date rules:
- Also outputs:
overall-result-YYYY-MM-DD.svgresource-distribution-YYYY-MM-DD.svg
YYYY-MM-DDsource:- Default: max
timestampdate in the data --data YYYY-MM-DD: only data on/before that date; use that date for snapshot and filenames
- Default: max
- If neither
--datenor--datais passed, also writes latest filenames:overall-result.svgresource-distribution.svg
Examples:
# Auto snapshot date from current data (also writes latest undated filenames)
node generate_statistic.js
# Fixed snapshot: data on/before the given date only
node generate_statistic.js --data 2026-01-14
batch-test.jscallsgenerate_statistic.jsafter the batch; no extra manual run needed.
Here test-results/ is a Git submodule—commit and push inside that directory:
cd test-results
git add .
git commit -m "Add site test result: example.com"
git pushTo show the new site on the public site (e.g.
https://resilience.ocf.tw/web/),
see the “From updated test results to a new public page” section in theweb-resilience-test-profileREADME.
Sites not in the automatic list can be added in manual_curated_list_tw.json:
[
{
"website": "example.com",
"url": "https://www.example.com"
},
{
"website": "another-site.org",
"url": "https://another-site.org"
}
]Notes:
website: primary domain (identifier)url: full URL to test (home or specific page)- After editing, run
generate_statistic.jsor batch tests to include them in stats
- Filenames: derived from the URL (usually strips
https://and trailing/, replaces/with_) - Re-runs: a new run overwrites the previous file for the same site
- Statistic order:
generate_statistic.jsprefersmerged_lists_tw.jsonorder; others append at the end - Submodule: if
test-results/is its own repo, commit/push there separately - Public site: updating
statistic.tsvdoes not update the public site—you must rebuild/deploy inweb-resilience-test-profile
Checking site: https://example.com
Collected X requests
Y unique domains after cleanup
Results:
-------------------
Domestic (O): 3
Cloud (?): 5
Foreign (X): 1
Details:
example.com: O (TW (HiNet))
cdn.example.com: - (US (GOOGLE))
api.example.com: X (US (Amazon))
During the ISIF research project (through 31 December 2026), this project is licensed under CC BY-NC-ND 4.0 International (Attribution—NonCommercial—NoDerivatives 4.0).
After 31 December 2026, project data and scripts will be released to the public domain. For uses beyond CC BY-NC-ND 4.0 during the research period, contact Irvin Chen (Open Culture Foundation; ORCID: https://orcid.org/0009-0002-1059-7130): irvin@ocf.tw (cc hi@ocf.tw).
Full terms and suggested attribution are in LICENSE. See also CITATION.cff for machine-readable citation metadata.
This work was supported by a grant from the APNIC Foundation (ROR: 01y4y6h16), via the Information Society Innovation Fund (ISIF Asia).