Repository: raviqqe/muffet
Pull request #535 (2025-06-26 08:52:33)

Added --ignore-network-errors to optionally ignore network errors
The idea of the option is similar to `--accepted-status-codes=200..300,403,429,500..599`. It prevents false link check errors when some remote host is temporarily down or times out a request (often happens when there are lots of external links), but still allows checking for 404 when the remote host is functioning.

The default behavior is not to ignore these errors, as it was before. It's possible to either ignore all network errors, or to only ignore errors on external hosts.


Patch:
diff --git a/arguments.go b/arguments.go
index 71195fb..308d4d7 100644
--- a/arguments.go
+++ b/arguments.go
@@ -22,9 +22,10 @@ type arguments struct {
 	FollowSitemapXML       bool     `long:"follow-sitemap-xml" description:"Scrape only pages listed in sitemap.xml (deprecated)"`
 	RawHeaders             []string `long:"header" value-name:"<header>..." description:"Custom headers"`
 	// TODO Remove a short option.
-	IgnoreFragments bool   `short:"f" long:"ignore-fragments" description:"Ignore URL fragments"`
-	DnsResolver     string `long:"dns-resolver" value-name:"<address>" description:"Custom DNS resolver"`
-	Format          string `long:"format" description:"Output format" default:"text" choice:"text" choice:"json" choice:"junit"`
+	IgnoreFragments     bool                `short:"f" long:"ignore-fragments" description:"Ignore URL fragments"`
+	IgnoreNetworkErrors ignoreNetworkErrors `long:"ignore-network-errors" description:"Ignore network errors and timeouts" choice:"none" choice:"all" choice:"external" default:"none"`
+	DnsResolver         string              `long:"dns-resolver" value-name:"<address>" description:"Custom DNS resolver"`
+	Format              string              `long:"format" description:"Output format" default:"text" choice:"text" choice:"json" choice:"junit"`
 	// TODO Remove this option.
 	JSONOutput bool `long:"json" description:"Output results in JSON (deprecated)"`
 	// TODO Remove this option.
diff --git a/command.go b/command.go
index bea32cb..a9bf5b7 100644
--- a/command.go
+++ b/command.go
@@ -112,6 +112,7 @@ func (c *command) runWithError(ss []string) (bool, error) {
 		f,
 		newLinkValidator(p.URL().Hostname(), rd, sm),
 		args.OnePageOnly,
+		args.IgnoreNetworkErrors,
 	)
 
 	go checker.Check(p)
diff --git a/doc/package-lock.json b/doc/package-lock.json
index 5f41d61..0615685 100644
--- a/doc/package-lock.json
+++ b/doc/package-lock.json
@@ -10,15 +10,15 @@
       "dependencies": {
         "@astrojs/sitemap": "^3.4.1",
         "@astrojs/starlight": "^0.34.4",
-        "@biomejs/biome": "^2.0.5",
-        "astro": "^5.10.1",
+        "@biomejs/biome": "^2.0.6",
+        "astro": "^5.10.2",
         "es-toolkit": "^1.39.5"
       },
       "devDependencies": {
         "@raviqqe/biome-config": "^2.0.2",
         "@raviqqe/eslint-config": "^4.1.16",
-        "@types/node": "^24.0.4",
-        "eslint": "^9.29.0",
+        "@types/node": "^24.0.8",
+        "eslint": "^9.30.0",
         "typescript": "^5.8.3"
       }
     },
@@ -227,10 +227,9 @@
       }
     },
     "node_modules/@biomejs/biome": {
-      "version": "2.0.5",
-      "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.0.5.tgz",
-      "integrity": "sha512-MztFGhE6cVjf3QmomWu83GpTFyWY8KIcskgRf2AqVEMSH4qI4rNdBLdpAQ11TNK9pUfLGz3IIOC1ZYwgBePtig==",
-      "license": "MIT OR Apache-2.0",
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.0.6.tgz",
+      "integrity": "sha512-RRP+9cdh5qwe2t0gORwXaa27oTOiQRQvrFf49x2PA1tnpsyU7FIHX4ZOFMtBC4QNtyWsN7Dqkf5EDbg4X+9iqA==",
       "bin": {
         "biome": "bin/biome"
       },
@@ -242,24 +241,23 @@
         "url": "https://opencollective.com/biome"
       },
       "optionalDependencies": {
-        "@biomejs/cli-darwin-arm64": "2.0.5",
-        "@biomejs/cli-darwin-x64": "2.0.5",
-        "@biomejs/cli-linux-arm64": "2.0.5",
-        "@biomejs/cli-linux-arm64-musl": "2.0.5",
-        "@biomejs/cli-linux-x64": "2.0.5",
-        "@biomejs/cli-linux-x64-musl": "2.0.5",
-        "@biomejs/cli-win32-arm64": "2.0.5",
-        "@biomejs/cli-win32-x64": "2.0.5"
+        "@biomejs/cli-darwin-arm64": "2.0.6",
+        "@biomejs/cli-darwin-x64": "2.0.6",
+        "@biomejs/cli-linux-arm64": "2.0.6",
+        "@biomejs/cli-linux-arm64-musl": "2.0.6",
+        "@biomejs/cli-linux-x64": "2.0.6",
+        "@biomejs/cli-linux-x64-musl": "2.0.6",
+        "@biomejs/cli-win32-arm64": "2.0.6",
+        "@biomejs/cli-win32-x64": "2.0.6"
       }
     },
     "node_modules/@biomejs/cli-darwin-arm64": {
-      "version": "2.0.5",
-      "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.0.5.tgz",
-      "integrity": "sha512-VIIWQv9Rcj9XresjCf3isBFfWjFStsdGZvm8SmwJzKs/22YQj167ge7DkxuaaZbNf2kmYif0AcjAKvtNedEoEw==",
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.0.6.tgz",
+      "integrity": "sha512-AzdiNNjNzsE6LfqWyBvcL29uWoIuZUkndu+wwlXW13EKcBHbbKjNQEZIJKYDc6IL+p7bmWGx3v9ZtcRyIoIz5A==",
       "cpu": [
         "arm64"
       ],
-      "license": "MIT OR Apache-2.0",
       "optional": true,
       "os": [
         "darwin"
@@ -269,13 +267,12 @@
       }
     },
     "node_modules/@biomejs/cli-darwin-x64": {
-      "version": "2.0.5",
-      "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.0.5.tgz",
-      "integrity": "sha512-DRpGxBgf5Z7HUFcNUB6n66UiD4VlBlMpngNf32wPraxX8vYU6N9cb3xQWOXIQVBBQ64QfsSLJnjNu79i/LNmSg==",
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.0.6.tgz",
+      "integrity": "sha512-wJjjP4E7bO4WJmiQaLnsdXMa516dbtC6542qeRkyJg0MqMXP0fvs4gdsHhZ7p9XWTAmGIjZHFKXdsjBvKGIJJQ==",
       "cpu": [
         "x64"
       ],
-      "license": "MIT OR Apache-2.0",
       "optional": true,
       "os": [
         "darwin"
@@ -285,13 +282,12 @@
       }
     },
     "node_modules/@biomejs/cli-linux-arm64": {
-      "version": "2.0.5",
-      "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.0.5.tgz",
-      "integrity": "sha512-FQTfDNMXOknf8+g9Eede2daaduRjTC2SNbfWPNFMadN9K3UKjeZ62jwiYxztPaz9zQQsZU8VbddQIaeQY5CmIA==",
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.0.6.tgz",
+      "integrity": "sha512-ZSVf6TYo5rNMUHIW1tww+rs/krol7U5A1Is/yzWyHVZguuB0lBnIodqyFuwCNqG9aJGyk7xIMS8HG0qGUPz0SA==",
       "cpu": [
         "arm64"
       ],
-      "license": "MIT OR Apache-2.0",
       "optional": true,
       "os": [
         "linux"
@@ -301,13 +297,12 @@
       }
     },
     "node_modules/@biomejs/cli-linux-arm64-musl": {
-      "version": "2.0.5",
-      "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.0.5.tgz",
-      "integrity": "sha512-OpflTCOw/ElEs7QZqN/HFaSViPHjAsAPxFJ22LhWUWvuJgcy/Z8+hRV0/3mk/ZRWy5A6fCDKHZqAxU+xB6W4mA==",
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.0.6.tgz",
+      "integrity": "sha512-CVPEMlin3bW49sBqLBg2x016Pws7eUXA27XYDFlEtponD0luYjg2zQaMJ2nOqlkKG9fqzzkamdYxHdMDc2gZFw==",
       "cpu": [
         "arm64"
       ],
-      "license": "MIT OR Apache-2.0",
       "optional": true,
       "os": [
         "linux"
@@ -317,13 +312,12 @@
       }
     },
     "node_modules/@biomejs/cli-linux-x64": {
-      "version": "2.0.5",
-      "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.0.5.tgz",
-      "integrity": "sha512-znpfydUDPuDkyBTulnODrQVK2FaG/4hIOPcQSsF2GeauQOYrBAOplj0etGB0NUrr0dFsvaQ15nzDXYb60ACoiw==",
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.0.6.tgz",
+      "integrity": "sha512-geM1MkHTV1Kh2Cs/Xzot9BOF3WBacihw6bkEmxkz4nSga8B9/hWy5BDiOG3gHDGIBa8WxT0nzsJs2f/hPqQIQw==",
       "cpu": [
         "x64"
       ],
-      "license": "MIT OR Apache-2.0",
       "optional": true,
       "os": [
         "linux"
@@ -333,13 +327,12 @@
       }
     },
     "node_modules/@biomejs/cli-linux-x64-musl": {
-      "version": "2.0.5",
-      "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.0.5.tgz",
-      "integrity": "sha512-9lmjCnajAzpZXbav2P6D87ugkhnaDpJtDvOH5uQbY2RXeW6Rq18uOUltxgacGBP+d8GusTr+s3IFOu7SN0Ok8g==",
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.0.6.tgz",
+      "integrity": "sha512-mKHE/e954hR/hSnAcJSjkf4xGqZc/53Kh39HVW1EgO5iFi0JutTN07TSjEMg616julRtfSNJi0KNyxvc30Y4rQ==",
       "cpu": [
         "x64"
       ],
-      "license": "MIT OR Apache-2.0",
       "optional": true,
       "os": [
         "linux"
@@ -349,13 +342,12 @@
       }
     },
     "node_modules/@biomejs/cli-win32-arm64": {
-      "version": "2.0.5",
-      "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.0.5.tgz",
-      "integrity": "sha512-CP2wKQB+gh8HdJTFKYRFETqReAjxlcN9AlYDEoye8v2eQp+L9v+PUeDql/wsbaUhSsLR0sjj3PtbBtt+02AN3A==",
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.0.6.tgz",
+      "integrity": "sha512-290V4oSFoKaprKE1zkYVsDfAdn0An5DowZ+GIABgjoq1ndhvNxkJcpxPsiYtT7slbVe3xmlT0ncdfOsN7KruzA==",
       "cpu": [
         "arm64"
       ],
-      "license": "MIT OR Apache-2.0",
       "optional": true,
       "os": [
         "win32"
@@ -365,13 +357,12 @@
       }
     },
     "node_modules/@biomejs/cli-win32-x64": {
-      "version": "2.0.5",
-      "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.0.5.tgz",
-      "integrity": "sha512-Sw3rz2m6bBADeQpr3+MD7Ch4E1l15DTt/+dfqKnwkm3cn4BrYwnArmvKeZdVsFRDjMyjlKIP88bw1r7o+9aqzw==",
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.0.6.tgz",
+      "integrity": "sha512-bfM1Bce0d69Ao7pjTjUS+AWSZ02+5UHdiAP85Th8e9yV5xzw6JrHXbL5YWlcEKQ84FIZMdDc7ncuti1wd2sdbw==",
       "cpu": [
         "x64"
       ],
-      "license": "MIT OR Apache-2.0",
       "optional": true,
       "os": [
         "win32"
@@ -873,11 +864,10 @@
       }
     },
     "node_modules/@eslint/config-array": {
-      "version": "0.20.1",
-      "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.1.tgz",
-      "integrity": "sha512-OL0RJzC/CBzli0DrrR31qzj6d6i6Mm3HByuhflhl4LOBiWxN+3i6/t/ZQQNii4tjksXi8r2CRW1wMpWA2ULUEw==",
+      "version": "0.21.0",
+      "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz",
+      "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==",
       "dev": true,
-      "license": "Apache-2.0",
       "dependencies": {
         "@eslint/object-schema": "^2.1.6",
         "debug": "^4.3.1",
@@ -888,11 +878,10 @@
       }
     },
     "node_modules/@eslint/config-helpers": {
-      "version": "0.2.1",
-      "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.1.tgz",
-      "integrity": "sha512-RI17tsD2frtDu/3dmI7QRrD4bedNKPM08ziRYaC5AhkGrzIAJelm9kJU1TznK+apx6V+cqRz8tfpEeG3oIyjxw==",
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.0.tgz",
+      "integrity": "sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==",
       "dev": true,
-      "license": "Apache-2.0",
       "engines": {
         "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
       }
@@ -948,11 +937,10 @@
       }
     },
     "node_modules/@eslint/js": {
-      "version": "9.29.0",
-      "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.29.0.tgz",
-      "integrity": "sha512-3PIF4cBw/y+1u2EazflInpV+lYsSG0aByVIQzAgb1m1MhHFSbqTyNqtBKHgWf/9Ykud+DhILS9EGkmekVhbKoQ==",
+      "version": "9.30.0",
+      "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.30.0.tgz",
+      "integrity": "sha512-Wzw3wQwPvc9sHM+NjakWTcPx11mbZyiYHuwWa/QfZ7cIRX7WK54PSk7bdyXDaoaopUcMatv1zaQvOAAO8hCdww==",
       "dev": true,
-      "license": "MIT",
       "engines": {
         "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
       },
@@ -965,7 +953,6 @@
       "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz",
       "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==",
       "dev": true,
-      "license": "Apache-2.0",
       "engines": {
         "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
       }
@@ -2101,10 +2088,9 @@
       }
     },
     "node_modules/@types/node": {
-      "version": "24.0.4",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.4.tgz",
-      "integrity": "sha512-ulyqAkrhnuNq9pB76DRBTkcS6YsmDALy6Ua63V8OhrOBgbcYt6IOdzpw5P1+dyRIyMerzLkeYWBeOXPpA9GMAA==",
-      "license": "MIT",
+      "version": "24.0.8",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.8.tgz",
+      "integrity": "sha512-WytNrFSgWO/esSH9NbpWUfTMGQwCGIKfCmNlmFDNiI5gGhgMmEA+V1AEvKLeBNvvtBnailJtkrEa2OIISwrVAA==",
       "dependencies": {
         "undici-types": "~7.8.0"
       }
@@ -2650,10 +2636,9 @@
       }
     },
     "node_modules/astro": {
-      "version": "5.10.1",
-      "resolved": "https://registry.npmjs.org/astro/-/astro-5.10.1.tgz",
-      "integrity": "sha512-DJVmt+51jU1xmgmAHCDwuUgcG/5aVFSU+tcX694acAZqPVt8EMUAmUZcJDX36Z7/EztnPph9HR3pm72jS2EgHQ==",
-      "license": "MIT",
+      "version": "5.10.2",
+      "resolved": "https://registry.npmjs.org/astro/-/astro-5.10.2.tgz",
+      "integrity": "sha512-CCBU+e/Apk6NWEMnc/R9dVZD/+FaCnNqWJicX1Oe6T18vLKop+LPs/m/88ekJk4zxP1g1N/GfUq4wEedUSk1Wg==",
       "dependencies": {
         "@astrojs/compiler": "^2.12.2",
         "@astrojs/internal-helpers": "0.6.1",
@@ -3903,19 +3888,18 @@
       }
     },
     "node_modules/eslint": {
-      "version": "9.29.0",
-      "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.29.0.tgz",
-      "integrity": "sha512-GsGizj2Y1rCWDu6XoEekL3RLilp0voSePurjZIkxL3wlm5o5EC9VpgaP7lrCvjnkuLvzFBQWB3vWB3K5KQTveQ==",
+      "version": "9.30.0",
+      "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.30.0.tgz",
+      "integrity": "sha512-iN/SiPxmQu6EVkf+m1qpBxzUhE12YqFLOSySuOyVLJLEF9nzTf+h/1AJYc1JWzCnktggeNrjvQGLngDzXirU6g==",
       "dev": true,
-      "license": "MIT",
       "dependencies": {
         "@eslint-community/eslint-utils": "^4.2.0",
         "@eslint-community/regexpp": "^4.12.1",
-        "@eslint/config-array": "^0.20.1",
-        "@eslint/config-helpers": "^0.2.1",
+        "@eslint/config-array": "^0.21.0",
+        "@eslint/config-helpers": "^0.3.0",
         "@eslint/core": "^0.14.0",
         "@eslint/eslintrc": "^3.3.1",
-        "@eslint/js": "9.29.0",
+        "@eslint/js": "9.30.0",
         "@eslint/plugin-kit": "^0.3.1",
         "@humanfs/node": "^0.16.6",
         "@humanwhocodes/module-importer": "^1.0.1",
diff --git a/doc/package.json b/doc/package.json
index 12ba45f..73fb746 100644
--- a/doc/package.json
+++ b/doc/package.json
@@ -14,15 +14,15 @@
   "dependencies": {
     "@astrojs/sitemap": "^3.4.1",
     "@astrojs/starlight": "^0.34.4",
-    "@biomejs/biome": "^2.0.5",
-    "astro": "^5.10.1",
+    "@biomejs/biome": "^2.0.6",
+    "astro": "^5.10.2",
     "es-toolkit": "^1.39.5"
   },
   "devDependencies": {
     "@raviqqe/biome-config": "^2.0.2",
     "@raviqqe/eslint-config": "^4.1.16",
-    "@types/node": "^24.0.4",
-    "eslint": "^9.29.0",
+    "@types/node": "^24.0.8",
+    "eslint": "^9.30.0",
     "typescript": "^5.8.3"
   }
 }
diff --git a/go.mod b/go.mod
index 892fb56..a594db2 100644
--- a/go.mod
+++ b/go.mod
@@ -15,7 +15,7 @@ require (
 	github.com/oxffaa/gopher-parse-sitemap v0.0.0-20191021113419-005d2eb1def4
 	github.com/stretchr/testify v1.10.0
 	github.com/temoto/robotstxt v1.1.2
-	github.com/valyala/fasthttp v1.62.0
+	github.com/valyala/fasthttp v1.63.0
 	github.com/yhat/scrape v0.0.0-20161128144610-24b7890b0945
 	go.uber.org/ratelimit v0.3.1
 	golang.org/x/net v0.41.0
diff --git a/go.sum b/go.sum
index b090976..8ae0033 100644
--- a/go.sum
+++ b/go.sum
@@ -38,8 +38,8 @@ github.com/temoto/robotstxt v1.1.2 h1:W2pOjSJ6SWvldyEuiFXNxz3xZ8aiWX5LbfDiOFd7Fx
 github.com/temoto/robotstxt v1.1.2/go.mod h1:+1AmkuG3IYkh1kv0d2qEB9Le88ehNO0zwOr3ujewlOo=
 github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
 github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
-github.com/valyala/fasthttp v1.62.0 h1:8dKRBX/y2rCzyc6903Zu1+3qN0H/d2MsxPPmVNamiH0=
-github.com/valyala/fasthttp v1.62.0/go.mod h1:FCINgr4GKdKqV8Q0xv8b+UxPV+H/O5nNFo3D+r54Htg=
+github.com/valyala/fasthttp v1.63.0 h1:DisIL8OjB7ul2d7cBaMRcKTQDYnrGy56R4FCiuDP0Ns=
+github.com/valyala/fasthttp v1.63.0/go.mod h1:REc4IeW+cAEyLrRPa5A81MIjvz0QE1laoTX2EaPHKJM=
 github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
 github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
 github.com/yhat/scrape v0.0.0-20161128144610-24b7890b0945 h1:6Ju8pZBYFTN9FaV/JvNBiIHcsgEmP4z4laciqjfjY8E=
diff --git a/ignore_network_errors.go b/ignore_network_errors.go
new file mode 100644
index 0000000..f547462
--- /dev/null
+++ b/ignore_network_errors.go
@@ -0,0 +1,9 @@
+package main
+
+type ignoreNetworkErrors string
+
+const (
+	ignoreNetworkErrorsNone     ignoreNetworkErrors = "none"
+	ignoreNetworkErrorsAll      ignoreNetworkErrors = "all"
+	ignoreNetworkErrorsExternal ignoreNetworkErrors = "external"
+)
diff --git a/page_checker.go b/page_checker.go
index 7205df0..9a1f94e 100644
--- a/page_checker.go
+++ b/page_checker.go
@@ -1,17 +1,24 @@
 package main
 
-import "sync"
+import (
+	"context"
+	"errors"
+	"net"
+	"net/url"
+	"sync"
+)
 
 type pageChecker struct {
-	fetcher       *linkFetcher
-	linkValidator *linkValidator
-	daemonManager *daemonManager
-	results       chan *pageResult
-	donePages     concurrentStringSet
-	onePageOnly   bool
+	fetcher             *linkFetcher
+	linkValidator       *linkValidator
+	daemonManager       *daemonManager
+	results             chan *pageResult
+	donePages           concurrentStringSet
+	onePageOnly         bool
+	ignoreNetworkErrors ignoreNetworkErrors
 }
 
-func newPageChecker(f *linkFetcher, v *linkValidator, onePageOnly bool) *pageChecker {
+func newPageChecker(f *linkFetcher, v *linkValidator, onePageOnly bool, ignore ignoreNetworkErrors) *pageChecker {
 	return &pageChecker{
 		f,
 		v,
@@ -19,6 +26,7 @@ func newPageChecker(f *linkFetcher, v *linkValidator, onePageOnly bool) *pageChe
 		make(chan *pageResult, concurrency),
 		newConcurrentStringSet(),
 		onePageOnly,
+		ignore,
 	}
 }
 
@@ -55,7 +63,7 @@ func (c *pageChecker) checkPage(p page) {
 
 			if err == nil {
 				sc <- &successLinkResult{u, status}
-			} else {
+			} else if !c.shouldIgnoreNetworkError(err, u) {
 				ec <- &errorLinkResult{u, err}
 			}
 
@@ -90,3 +98,29 @@ func (c *pageChecker) addPage(p page) {
 		c.daemonManager.Add(func() { c.checkPage(p) })
 	}
 }
+
+func (c *pageChecker) shouldIgnoreNetworkError(err error, rawURL string) bool {
+	if c.ignoreNetworkErrors == ignoreNetworkErrorsNone {
+		return false
+	}
+
+	if !isNetworkError(err) {
+		return false
+	}
+
+	if c.ignoreNetworkErrors == ignoreNetworkErrorsAll {
+		return true
+	}
+
+	u, parseErr := url.Parse(rawURL)
+	if parseErr != nil {
+		return false
+	}
+
+	return u.Hostname() != c.linkValidator.hostname
+}
+
+func isNetworkError(err error) bool {
+	var netErr net.Error
+	return errors.As(err, &netErr) || errors.Is(err, context.DeadlineExceeded)
+}


Test patch:
diff --git a/.snapshots/TestHelp b/.snapshots/TestHelp
index fbddea1..255f166 100644
--- a/.snapshots/TestHelp
+++ b/.snapshots/TestHelp
@@ -2,45 +2,53 @@ Usage:
   muffet.test [options] <url>
 
 Application Options:
-      --accepted-status-codes=<codes>       Accepted HTTP response status codes
-                                            (e.g. '200..300,403') (default:
-                                            200..300)
-  -b, --buffer-size=<size>                  HTTP response buffer size in bytes
-                                            (default: 4096)
-  -c, --max-connections=<count>             Maximum number of HTTP connections
-                                            (default: 512)
-      --max-connections-per-host=<count>    Maximum number of HTTP connections
-                                            per host (default: 512)
-      --max-response-body-size=<size>       Maximum response body size to read
-                                            (default: 10000000)
-  -e, --exclude=<pattern>...                Exclude URLs matched with given
-                                            regular expressions
-  -i, --include=<pattern>...                Include URLs matched with given
-                                            regular expressions
-      --follow-robots-txt                   Follow robots.txt when scraping
-                                            pages
-      --follow-sitemap-xml                  Scrape only pages listed in
-                                            sitemap.xml (deprecated)
-      --header=<header>...                  Custom headers
-  -f, --ignore-fragments                    Ignore URL fragments
-      --dns-resolver=<address>              Custom DNS resolver
-      --format=[text|json|junit]            Output format (default: text)
-      --json                                Output results in JSON (deprecated)
-      --experimental-verbose-json           Include successful results in JSON
-                                            (deprecated)
-      --junit                               Output results as JUnit XML file
-                                            (deprecated)
-  -r, --max-redirections=<count>            Maximum number of redirections
-                                            (default: 64)
-      --rate-limit=<rate>                   Max requests per second
-  -t, --timeout=<seconds>                   Timeout for HTTP requests in
-                                            seconds (default: 10)
-  -v, --verbose                             Show successful results too
-      --proxy=<host>                        HTTP proxy host
-      --skip-tls-verification               Skip TLS certificate verification
-      --one-page-only                       Only check links found in the given
-                                            URL
-      --color=[auto|always|never]           Color output (default: auto)
-  -h, --help                                Show this help
-      --version                             Show version
+      --accepted-status-codes=<codes>                Accepted HTTP response
+                                                     status codes (e.g.
+                                                     '200..300,403') (default:
+                                                     200..300)
+  -b, --buffer-size=<size>                           HTTP response buffer size
+                                                     in bytes (default: 4096)
+  -c, --max-connections=<count>                      Maximum number of HTTP
+                                                     connections (default: 512)
+      --max-connections-per-host=<count>             Maximum number of HTTP
+                                                     connections per host
+                                                     (default: 512)
+      --max-response-body-size=<size>                Maximum response body size
+                                                     to read (default: 10000000)
+  -e, --exclude=<pattern>...                         Exclude URLs matched with
+                                                     given regular expressions
+  -i, --include=<pattern>...                         Include URLs matched with
+                                                     given regular expressions
+      --follow-robots-txt                            Follow robots.txt when
+                                                     scraping pages
+      --follow-sitemap-xml                           Scrape only pages listed
+                                                     in sitemap.xml (deprecated)
+      --header=<header>...                           Custom headers
+  -f, --ignore-fragments                             Ignore URL fragments
+      --ignore-network-errors=[none|all|external]    Ignore network errors and
+                                                     timeouts (default: none)
+      --dns-resolver=<address>                       Custom DNS resolver
+      --format=[text|json|junit]                     Output format (default:
+                                                     text)
+      --json                                         Output results in JSON
+                                                     (deprecated)
+      --experimental-verbose-json                    Include successful results
+                                                     in JSON (deprecated)
+      --junit                                        Output results as JUnit
+                                                     XML file (deprecated)
+  -r, --max-redirections=<count>                     Maximum number of
+                                                     redirections (default: 64)
+      --rate-limit=<rate>                            Max requests per second
+  -t, --timeout=<seconds>                            Timeout for HTTP requests
+                                                     in seconds (default: 10)
+  -v, --verbose                                      Show successful results too
+      --proxy=<host>                                 HTTP proxy host
+      --skip-tls-verification                        Skip TLS certificate
+                                                     verification
+      --one-page-only                                Only check links found in
+                                                     the given URL
+      --color=[auto|always|never]                    Color output (default:
+                                                     auto)
+  -h, --help                                         Show this help
+      --version                                      Show version
 
diff --git a/arguments_test.go b/arguments_test.go
index 983ad69..d57ebda 100644
--- a/arguments_test.go
+++ b/arguments_test.go
@@ -35,6 +35,8 @@ func TestGetArguments(t *testing.T) {
 		{"--verbose", "https://foo.com"},
 		{"-v", "-f", "https://foo.com"},
 		{"-v", "--ignore-fragments", "https://foo.com"},
+		{"--ignore-network-errors", "all", "https://foo.com"},
+		{"--ignore-network-errors", "external", "https://foo.com"},
 		{"--one-page-only", "https://foo.com"},
 		{"--json", "https://foo.com"},
 		{"-h"},
@@ -61,6 +63,7 @@ func TestGetArgumentsError(t *testing.T) {
 		{"--max-redirections", "foo", "https://foo.com"},
 		{"-t", "foo", "https://foo.com"},
 		{"--timeout", "foo", "https://foo.com"},
+		{"--ignore-network-errors", "foo", "https://foo.com"},
 	} {
 		_, err := getArguments(ss)
 		assert.NotNil(t, err)
diff --git a/page_checker_test.go b/page_checker_test.go
index 82a2a86..e39d3b4 100644
--- a/page_checker_test.go
+++ b/page_checker_test.go
@@ -9,6 +9,10 @@ import (
 )
 
 func newTestPageChecker(c *fakeHttpClient) *pageChecker {
+	return newTestPageCheckerWithIgnore(c, ignoreNetworkErrorsNone)
+}
+
+func newTestPageCheckerWithIgnore(c *fakeHttpClient, ignore ignoreNetworkErrors) *pageChecker {
 	return newPageChecker(
 		newLinkFetcher(
 			c,
@@ -17,9 +21,16 @@ func newTestPageChecker(c *fakeHttpClient) *pageChecker {
 		),
 		newLinkValidator("foo.com", nil, nil),
 		false,
+		ignore,
 	)
 }
 
+type fakeNetError struct{}
+
+func (fakeNetError) Error() string   { return "network error" }
+func (fakeNetError) Timeout() bool   { return true }
+func (fakeNetError) Temporary() bool { return true }
+
 func newTestPage(t *testing.T, fragments map[string]struct{}, links map[string]error) page {
 	u, err := url.Parse("http://foo.com")
 	assert.Nil(t, err)
@@ -112,3 +123,57 @@ func TestPageCheckerDoNotCheckSamePageTwice(t *testing.T) {
 
 	assert.Equal(t, 1, i)
 }
+
+func TestPageCheckerIgnoreNetworkErrorsAll(t *testing.T) {
+	c := newTestPageCheckerWithIgnore(
+		newFakeHttpClient(
+			func(u *url.URL) (*fakeHttpResponse, error) {
+				if u.String() == "http://foo.com" {
+					return newFakeHtmlResponse(
+						"http://foo.com",
+						`<html><body><a href="http://bar.com" /></body></html>`,
+					), nil
+				}
+
+				return nil, fakeNetError{}
+			},
+		),
+		ignoreNetworkErrorsAll,
+	)
+
+	go c.Check(newTestPage(t, nil, map[string]error{"http://bar.com": nil}))
+
+	r := <-c.Results()
+	assert.True(t, r.OK())
+	assert.Equal(t, 0, len(r.ErrorLinkResults))
+}
+
+func TestPageCheckerIgnoreNetworkErrorsExternal(t *testing.T) {
+	c := newTestPageCheckerWithIgnore(
+		newFakeHttpClient(func(u *url.URL) (*fakeHttpResponse, error) {
+			return nil, fakeNetError{}
+		}),
+		ignoreNetworkErrorsExternal,
+	)
+
+	go c.Check(newTestPage(t, nil, map[string]error{"http://bar.com": nil}))
+
+	r := <-c.Results()
+	assert.True(t, r.OK())
+	assert.Equal(t, 0, len(r.ErrorLinkResults))
+}
+
+func TestPageCheckerDoNotIgnoreInternalNetworkErrors(t *testing.T) {
+	c := newTestPageCheckerWithIgnore(
+		newFakeHttpClient(func(u *url.URL) (*fakeHttpResponse, error) {
+			return nil, fakeNetError{}
+		}),
+		ignoreNetworkErrorsExternal,
+	)
+
+	go c.Check(newTestPage(t, nil, map[string]error{"http://foo.com/foo": nil}))
+
+	r := <-c.Results()
+	assert.False(t, r.OK())
+	assert.Equal(t, 1, len(r.ErrorLinkResults))
+}