diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 000000000..5d47c21c4
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,12 @@
+# EditorConfig is awesome: https://EditorConfig.org
+
+# top-most EditorConfig file
+root = true
+
+[*]
+indent_style = space
+indent_size = 2
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
diff --git a/.eleventy.js b/.eleventy.js
index 096e6fb9a..2ce0822f7 100644
--- a/.eleventy.js
+++ b/.eleventy.js
@@ -12,10 +12,12 @@ const drafts = [
   'latest'
 ];
 
-module.exports = function(eleventyConfig) {
+export default async function(eleventyConfig) {
   eleventyConfig.addPassthroughCopy('404.html');
   eleventyConfig.addPassthroughCopy('.htaccess');
   eleventyConfig.addPassthroughCopy('LICENSE.md');
+  eleventyConfig.addPassthroughCopy('_headers');
+  eleventyConfig.addPassthroughCopy('_redirects');
   eleventyConfig.addPassthroughCopy('benchmarks/**/*.{jsonld,nq,md}');
   eleventyConfig.addPassthroughCopy('contexts/**/*.{htaccess,html,jsonld}');
   eleventyConfig.addPassthroughCopy('contexts/{event,person,place,recipe,remote-context}');
@@ -35,13 +37,13 @@ module.exports = function(eleventyConfig) {
   }
   eleventyConfig.addPassthroughCopy('static');
   eleventyConfig.addPassthroughCopy('test-suite');
-  eleventyConfig.addPassthroughCopy('utils');
   eleventyConfig.ignores.add('CONTRIBUTING.md');
   eleventyConfig.ignores.add('LICENSE.md');
   eleventyConfig.ignores.add('README.md');
   eleventyConfig.ignores.add('benchmarks/README.md');
   eleventyConfig.ignores.add('contexts/person.html');
   eleventyConfig.ignores.add('examples');
+  eleventyConfig.ignores.add('images/Makefile');
   eleventyConfig.ignores.add('images/README.md');
   eleventyConfig.ignores.add('minutes/**/*');
   eleventyConfig.ignores.add('ns/json-ld.html');
diff --git a/.gitignore b/.gitignore
index b64c927ed..0d5760310 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,6 +3,7 @@
 *.sw[op]
 .DS_Store
 .wrangler
-node_modules
+node_modules/
 playground/jsonld.js
-_site
+_site/
+package-lock.json
diff --git a/.htaccess b/.htaccess
deleted file mode 100644
index 84c18f9ff..000000000
--- a/.htaccess
+++ /dev/null
@@ -1,7 +0,0 @@
-<FilesMatch "(\.jsonld)$">
-   ForceType application/ld+json
-   SetHandler default_handler
-   Header set Access-Control-Allow-Origin "*"
-</FilesMatch>
-
-Redirect 302 /playground-dev /playground
diff --git a/404.html b/404.html
index b9ba71943..49526412b 100644
--- a/404.html
+++ b/404.html
@@ -16,15 +16,15 @@
     <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
 
     <!-- Style Sheets -->
-    <link rel="stylesheet" type="text/css" href="static/css/bootstrap/bootstrap.css">
-    <link rel="stylesheet" type="text/css" href="static/css/bootstrap/bootstrap-responsive.css">
-    <link rel="stylesheet" type="text/css" href="static/css/bootstrap/font-awesome.css">
+    <link rel="stylesheet" type="text/css" href="/static/css/bootstrap/bootstrap.css">
+    <link rel="stylesheet" type="text/css" href="/static/css/bootstrap/bootstrap-responsive.css">
+    <link rel="stylesheet" type="text/css" href="/static/css/bootstrap/font-awesome.css">
     <style>
       /** bootstrap 2.3.2 overrides **/
       .alert.alert-success a {color: #3d9400; text-decoration: underline }
     </style>
 
-    <link rel="shortcut icon" href="favicon.ico" />
+    <link rel="shortcut icon" href="/favicon.ico" />
 
     <!-- Script tags -->
     <script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script>
@@ -53,10 +53,10 @@
           <div class="nav-collapse">
           <ul class="nav">
             <li>
-              <a href="playground/"><span class="icon-beer"></span> Playground</a>
+              <a href="/playground/"><span class="icon-beer"></span> Playground</a>
             </li>
             <li>
-              <a href="learn/"><span class="icon-book"></span> Documentation</a>
+              <a href="/learn/"><span class="icon-book"></span> Documentation</a>
             </li>
             <li><a href="#developers"><span class="icon-beaker"></span> Developers</a></li>
             <li class="dropdown">
@@ -82,7 +82,7 @@
                   <li><a href="/spec/">1.0 drafts (historic)</a></li>
               </ul>
             </li>
-            <li><a href="images/"><span class="icon-picture"></span> Branding</a></li>
+            <li><a href="/images/"><span class="icon-picture"></span> Branding</a></li>
             </ul>
           </div>
           <!--/.nav-collapse -->
diff --git a/README.md b/README.md
index 34cd6856f..cd9a3bcc7 100644
--- a/README.md
+++ b/README.md
@@ -74,10 +74,12 @@ To develop this website locally:
 ```sh
 # install dependencies
 npm i
-# to rebuild on changes and run a server:
-npm run serve
-# to rebuild on changes:
+# to just build the static files to `_site/`
+npm run build
+# to rebuild the files on changes
 npm run watch
+# to serve `_site/` with Cloudflare Pages feature support
+npm run pages # visit http://localhost:8788/
 ```
 
 Additionally, if you want to use or test the playground `http:` proxy, also run
diff --git a/_headers b/_headers
new file mode 100644
index 000000000..8cede675b
--- /dev/null
+++ b/_headers
@@ -0,0 +1,30 @@
+/contexts/event
+    Content-Type: application/ld+json
+    Access-Control-Allow-Origin: "*"
+/contexts/person
+    Content-Type: application/ld+json
+    Access-Control-Allow-Origin: "*"
+/contexts/place
+    Content-Type: application/ld+json
+    Access-Control-Allow-Origin: "*"
+/contexts/recipe
+    Content-Type: application/ld+json
+    Access-Control-Allow-Origin: "*"
+
+# Tests 0009-0011 Add link header
+/test-suite/tests/remote-doc-0009-in.jsonld
+    Link: <remote-doc-0009-context.jsonld>; rel="http://www.w3.org/ns/json-ld#context"
+/test-suite/tests/remote-doc-0010-in.json
+    Link: <remote-doc-0010-context.jsonld>; rel="http://www.w3.org/ns/json-ld#context"
+/test-suite/tests/remote-doc-0011-in.jldt
+    Link: <remote-doc-0011-context.jsonld>; rel="http://www.w3.org/ns/json-ld#context"
+
+# Test 00012 adds multiple link headers
+/test-suite/tests/remote-doc-0012-in.json
+    Link: <remote-doc-0012-context1.jsonld>; rel="http://www.w3.org/ns/json-ld#context"
+    Link: <remote-doc-0012-context2.jsonld>; rel="http://www.w3.org/ns/json-ld#context"
+
+/test-suite/tests/*.jldt
+    Content-Type: application/jldTest+json
+/test-suite/tests/*.jldte
+    Content-Type: application/jldTest
diff --git a/_redirects b/_redirects
new file mode 100644
index 000000000..719487ebe
--- /dev/null
+++ b/_redirects
@@ -0,0 +1,20 @@
+/playground-dev /playground 302
+
+/contexts/schema.org.jsonld https://schema.org/ 301
+
+/spec/latest/json-ld-syntax https://www.w3.org/TR/json-ld/ 301
+/spec/latest/json-ld-syntax/ https://www.w3.org/TR/json-ld/ 301
+
+# Tests 0005-0007, status redirect to 0001
+/test-suite/remote-doc-0005-in.jsonld /test-suite/tests/remote-doc-0001-in.jsonld 301
+/test-suite/remote-doc-0006-in.jsonld /test-suite/tests/remote-doc-0001-in.jsonld 303
+/test-suite/remote-doc-0007-in.jsonld /test-suite/tests/remote-doc-0001-in.jsonld 307
+
+/spec/latest/rdf-graph-normalization https://w3c-ccg.github.io/rdf-dataset-canonicalization/spec/ 307
+/spec/latest/rdf-graph-normalization/* https://w3c-ccg.github.io/rdf-dataset-canonicalization/spec/:splat 307
+/spec/latest/rdf-dataset-normalization/* https://w3c-ccg.github.io/rdf-dataset-canonicalization/spec/:splat 307
+/spec/latest/rdf-dataset-canonicalization/* https://w3c-ccg.github.io/rdf-dataset-canonicalization/spec/:splat 307
+
+/spec/latest/json-ld/* https://www.w3.org/TR/json-ld/:splat 301
+/spec/latest/json-ld-api/* https://www.w3.org/TR/json-ld-api/:splat 301
+/spec/latest/json-ld-framing/* https://www.w3.org/TR/json-ld-framing/:splat 301
diff --git a/contexts/.htaccess b/contexts/.htaccess
deleted file mode 100644
index a8f948ec7..000000000
--- a/contexts/.htaccess
+++ /dev/null
@@ -1,7 +0,0 @@
-<FilesMatch "(event|person|place|recipe|\.jsonld)$">
-   ForceType application/ld+json
-   SetHandler default_handler
-   Header set Access-Control-Allow-Origin "*"
-</FilesMatch>
-
-Redirect 301 /contexts/schema.org.jsonld http://schema.org/
diff --git a/functions/test-suite/_middleware.js b/functions/test-suite/_middleware.js
new file mode 100644
index 000000000..bc2db3b83
--- /dev/null
+++ b/functions/test-suite/_middleware.js
@@ -0,0 +1,57 @@
+function parseAcceptHeader(acceptHeader) {
+  // generated by Google AI "parse accept header javascript"
+  if (!acceptHeader) {
+    return [];
+  }
+
+  return acceptHeader.split(',')
+    .map(item => item.trim())
+    .map(item => {
+      const parts = item.split(';');
+      const value = parts[0].trim();
+      let quality = 1;
+
+      if (parts.length > 1) {
+        const q = parts[1].trim();
+        if (q.startsWith('q=')) {
+          quality = parseFloat(q.substring(2));
+        }
+      }
+
+      return { value, quality };
+    })
+    .sort((a, b) => b.quality - a.quality);
+    // From:
+    // text/html, application/xhtml+xml, application/xml;q=0.9, */*;q=0.8
+    // Expected output:
+    // [
+    //   { value: 'text/html', quality: 1 },
+    //   { value: 'application/xhtml+xml', quality: 1 },
+    //   { value: 'application/xml', quality: 0.9 },
+    //   { value: '*/*', quality: 0.8 }
+    // ]
+}
+
+const typeToExt = {
+  "text/turtle": "ttl",
+  "application/ld+json": "jsonld"
+};
+
+export async function onRequest(context) {
+  try {
+    const parsedAccept = parseAcceptHeader(context.request.headers.get('Accept'));
+    const accept = parsedAccept[0].value;
+
+    // if we have a mapping for this extension, send it. Otherwise fall through.
+    if(Object.keys(typeToExt).indexOf(accept) > -1) {
+      const ext = typeToExt[accept] || `html`;
+      const rewrittenUrl = context.request.url + '.' + ext;
+      const asset = await context.env.ASSETS.fetch(rewrittenUrl);
+      const response = new Response(asset.body, asset);
+      return response;
+    }
+    return context.next();
+  } catch (err) {
+    return new Response(`${err.message}\n${err.stack}`, { status: 500 });
+  }
+}
diff --git a/images/.htaccess b/images/.htaccess
deleted file mode 100644
index ee8e4d25e..000000000
--- a/images/.htaccess
+++ /dev/null
@@ -1,10 +0,0 @@
-<Files Makefile>
-  order allow,deny
-  deny from all
-</Files>
-
-<Files README.md>
-  order allow,deny
-  deny from all
-</Files>
-
diff --git a/package.json b/package.json
index fc7e4bbd4..a1e31927a 100644
--- a/package.json
+++ b/package.json
@@ -2,14 +2,16 @@
   "name": "json-ld.org",
   "private": true,
   "description": "json-ld.org homepage",
+  "type": "module",
   "scripts": {
     "build": "eleventy",
-    "serve": "eleventy --serve",
+    "pages": "wrangler pages dev _site/ --compatibility-date=2025-04-02",
     "watch": "eleventy --watch",
     "dev": "wrangler pages dev _site --compatibility-date=2024-04-27 --live-reload --port 8788",
     "test": "echo \"Error: no test specified\" && exit 1"
   },
   "devDependencies": {
-    "@11ty/eleventy": "^3.0.0-alpha.9"
+    "@11ty/eleventy": "^3.0.0",
+    "wrangler": "^4.6.0"
   }
 }
diff --git a/requirements/index.11tydata.js b/requirements/index.11tydata.js
index 5d9597f64..a3650db65 100644
--- a/requirements/index.11tydata.js
+++ b/requirements/index.11tydata.js
@@ -1,5 +1,8 @@
-const fs = require('node:fs/promises');
-const path = require('node:path');
+import { fileURLToPath } from 'url';
+import fs from 'node:fs/promises';
+import path from 'node:path';
+
+const __dirname = path.dirname(fileURLToPath(import.meta.url));
 
 const specStatuses = ['ED'];
 
@@ -35,7 +38,7 @@ const specs = [
   'requirements'
 ];
 
-module.exports = async function() {
+export default async function() {
   return {
     specs: Object.fromEntries(await Promise.all(specs.map(async spec => {
       return [spec, await getDrafts(spec)];
diff --git a/spec/ED/.htaccess b/spec/ED/.htaccess
deleted file mode 100644
index 3b8be4f70..000000000
--- a/spec/ED/.htaccess
+++ /dev/null
@@ -1,3 +0,0 @@
-RewriteEngine on
-RewriteBase /json-ld.org/spec/ED
-RewriteRule ^(20.*) json-ld-syntax/$1 [R=301,L]
diff --git a/spec/index.11tydata.js b/spec/index.11tydata.js
index 83967fda0..a54ae4710 100644
--- a/spec/index.11tydata.js
+++ b/spec/index.11tydata.js
@@ -1,5 +1,8 @@
-const fs = require('node:fs/promises');
-const path = require('node:path');
+import { fileURLToPath } from 'url';
+import fs from 'node:fs/promises';
+import path from 'node:path';
+
+const __dirname = path.dirname(fileURLToPath(import.meta.url));
 
 const specStatuses = ['ED', 'FCGS', 'WD', 'CR', 'PR', 'REC'/*, 'CG-FINAL'*/];
 
@@ -33,14 +36,14 @@ async function getDrafts(spec) {
 
 const specs = [
   'json-ld',
-  'json-ld-syntax', 
-  'json-ld-api', 
-  'json-ld-api-best-practices', 
-  'json-ld-framing', 
+  'json-ld-syntax',
+  'json-ld-api',
+  'json-ld-api-best-practices',
+  'json-ld-framing',
   'json-ld-rdf'
 ];
 
-module.exports = async function() {
+export default async function() {
   return {
     specs: Object.fromEntries(await Promise.all(specs.map(async spec => {
       return [spec, await getDrafts(spec)];
diff --git a/spec/latest/.htaccess b/spec/latest/.htaccess
deleted file mode 100644
index af6788fc5..000000000
--- a/spec/latest/.htaccess
+++ /dev/null
@@ -1,12 +0,0 @@
-RewriteEngine On
-
-RewriteBase /spec/latest/
-RewriteRule ^json-ld-syntax(.*) json-ld$1 [R=301,NC,L]
-
-RewriteRule ^json-ld/(.*) https://www.w3.org/TR/json-ld/$1 [R=301,NC,L]
-RewriteRule ^json-ld-api/(.*) https://www.w3.org/TR/json-ld-api/$1 [R=301,NC,L]
-RewriteRule ^json-ld-framing/(.*) https://www.w3.org/TR/json-ld-framing/$1 [R=301,NC,L]
-
-RewriteRule ^rdf-graph-normalization(.*) https://w3c-ccg.github.io/rdf-dataset-canonicalization/spec/$1 [R=307,NC,L]
-RewriteRule ^rdf-dataset-normalization/(.*) https://w3c-ccg.github.io/rdf-dataset-canonicalization/spec/$1 [R=307,NC,L]
-RewriteRule ^rdf-dataset-canonicalization/(.*) https://w3c-ccg.github.io/rdf-dataset-canonicalization/spec/$1 [R=307,NC,L]
diff --git a/test-redirects.js b/test-redirects.js
new file mode 100644
index 000000000..f0775ed05
--- /dev/null
+++ b/test-redirects.js
@@ -0,0 +1,116 @@
+import assert from 'node:assert/strict';
+import fs from 'node:fs/promises';
+
+const baseUri = 'http://localhost:8788';
+
+// Test _headers
+(async () => {
+  // test json-ld media type
+  const url = `${baseUri}/contexts/event.jsonld`;
+  const resp = await fetch(url);
+  assert(resp.headers.get('Content-Type') === 'application/ld+json',
+    `Content-Type for ${url} should be application/ld+json.`);
+})();
+
+(async () => {
+  // test json-ld media type on URLs without extensions
+  const urls = [
+    `${baseUri}/contexts/event`,
+    `${baseUri}/contexts/person`,
+    `${baseUri}/contexts/place`,
+    `${baseUri}/contexts/recipe`,
+  ];
+  Promise.all(urls.map(async (url) => {
+    const resp = await fetch(url, {redirect: 'manual'});
+    const contentType = resp.headers.get('Content-Type');
+    assert(contentType === 'application/ld+json',
+      `Content-Type for ${url} should be application/ld+json; got ${contentType}.`);
+    const cors = resp.headers.get('Access-Control-Allow-Origin');
+    assert(cors === '"*"',
+      `Header 'Access-Control-Allow-Origin' should be '*'; got ${cors}.`);
+  }));
+})();
+
+(async () => {
+  // test json-ld media type
+  const urls = [
+    `${baseUri}/contexts/event`,
+    `${baseUri}/contexts/person`,
+    `${baseUri}/contexts/place`,
+    `${baseUri}/contexts/recipe`,
+  ];
+  Promise.all(urls.map(async (url) => {
+    const resp = await fetch(url, {redirect: 'manual'});
+    const contentType = resp.headers.get('Content-Type');
+    assert(contentType === 'application/ld+json',
+      `Content-Type for ${url} should be application/ld+json; got ${contentType}.`);
+    const cors = resp.headers.get('Access-Control-Allow-Origin');
+    assert(cors === '"*"',
+      `Header 'Access-Control-Allow-Origin' should be '*'; got ${cors}.`);
+  }));
+})();
+
+(async () => {
+  // test media type for `*.jldte`
+  const url = `${baseUri}/test-suite/tests/remote-doc-0003-in.jldt`;
+  const resp = await fetch(url);
+  const contentType = resp.headers.get('Content-Type');
+  assert(contentType === 'application/jldTest+json',
+    `Content-Type for ${url} should be application/jldTest+json; got ${contentType}.`);
+})();
+
+(async () => {
+  // test media type for `*.jldte`
+  const url = `${baseUri}/test-suite/tests/remote-doc-0004-in.jldte`;
+  const resp = await fetch(url);
+  const contentType = resp.headers.get('Content-Type');
+  assert(contentType === 'application/jldTest',
+    `Content-Type for ${url} should be application/jldTest; got ${contentType}.`);
+})();
+
+(async () => {
+  // test link headers
+  const urlsToLinkHeaderValues = {
+    '/test-suite/tests/remote-doc-0009-in.jsonld':
+      `<remote-doc-0009-context.jsonld>; rel="http://www.w3.org/ns/json-ld#context"`,
+    '/test-suite/tests/remote-doc-0010-in.json':
+      `<remote-doc-0010-context.jsonld>; rel="http://www.w3.org/ns/json-ld#context"`,
+    '/test-suite/tests/remote-doc-0011-in.jldt':
+      `<remote-doc-0011-context.jsonld>; rel="http://www.w3.org/ns/json-ld#context"`,
+    '/test-suite/tests/remote-doc-0012-in.json':
+      `<remote-doc-0012-context1.jsonld>; rel="http://www.w3.org/ns/json-ld#context", <remote-doc-0012-context2.jsonld>; rel="http://www.w3.org/ns/json-ld#context"`
+  };
+  Promise.all(Object.entries(urlsToLinkHeaderValues)
+    .map(async ([absoultePath, intendedHeaderValue]) => {
+      const url = `${baseUri}${absoultePath}`;
+      const resp = await fetch(url);
+      const actualLinkValue = resp.headers.get('Link');
+      assert(actualLinkValue === intendedHeaderValue,
+        `Link header for ${url} should be ${intendedHeaderValue}; got ${actualLinkValue}.`);
+    })
+  );
+})();
+
+// Test _redirects
+(async () => {
+  const _redirects = await fs.readFile('./_redirects', 'utf-8');
+  Promise.all(_redirects.split('\n')
+    .filter((v) => (v[0] !== '#') ? v : null)
+    .map(async (line) => {
+      let [source, destination, code] = line.split(/\s/);
+      if(source.endsWith('*')) {
+        // remove *
+        source = source.slice(0, source.length - 1);
+        // remove :splat
+        destination = destination.slice(0, destination.length - 6);
+      }
+      const url = `${baseUri}${source}`;
+      const resp = await fetch(url, {redirect: 'manual'});
+      assert(resp.status === code || 302,
+        `Should be a 302 redirect.`);
+      const location = resp.headers.get('Location');
+      assert(location === destination,
+        `Old ${source} should redirect to ${destination}, got ${location}`);
+    })
+  );
+})();
diff --git a/test-suite/.htaccess b/test-suite/.htaccess
deleted file mode 100644
index 9ca4fb57e..000000000
--- a/test-suite/.htaccess
+++ /dev/null
@@ -1,38 +0,0 @@
-<FilesMatch "(\.jsonld)$">
-   ForceType application/ld+json
-   SetHandler default_handler
-   Header set Access-Control-Allow-Origin "*"
-</FilesMatch>
-
-# Turn off MultiViews
-Options -MultiViews
-
-# Directive to ensure *.ttl and .jsonld files served appropriately
-AddType text/turtle .ttl
-AddType application/ld+json .jsonld
-AddType application/n-quads .nq
-
-# Rewrite engine setup
-RewriteEngine On
-RewriteBase /test-suite
-
-# Rewrite rule to serve HTML content from the vocabulary URI if requested
-RewriteCond %{HTTP_ACCEPT} !application/rdf\+xml.*(text/html|application/xhtml\+xml)
-RewriteCond %{HTTP_ACCEPT} text/html [OR]
-RewriteCond %{HTTP_ACCEPT} application/xhtml\+xml [OR]
-RewriteCond %{HTTP_USER_AGENT} ^Mozilla/.*
-RewriteRule ^vocab$ vocab.html [R=303]
-
-# Rewrite rule to serve Turtle content from the vocabulary URI if requested
-RewriteCond %{HTTP_ACCEPT} text/turtle
-RewriteRule ^vocab$ vocab.ttl [R=303]
-
-# Rewrite rule to serve JSON-LD content from the vocabulary URI if requested
-RewriteCond %{HTTP_ACCEPT} application/ld+json
-RewriteRule ^vocab$ vocab.jsonld [R=303]
-
-# Choose the default response
-# ---------------------------
-
-# Rewrite rule to serve the HTML content from the vocabulary URI by default
-RewriteRule ^vocab$ vocab.html [R=303]
diff --git a/test-suite/tests/.htaccess b/test-suite/tests/.htaccess
deleted file mode 100644
index 45a04ae85..000000000
--- a/test-suite/tests/.htaccess
+++ /dev/null
@@ -1,30 +0,0 @@
-# Special rules for document loader tests
-# Rewrite engine setup
-RewriteEngine On
-RewriteBase /test-suite
-
-# Add directive for test types
-AddType application/jldTest+json .jldt
-AddType application/jldTest .jldte
-
-# Tests 0005-0007, status redirect to 0001
-RewriteRule ^remote-doc-0005-in.jsonld$ tests/remote-doc-0001-in.jsonld [R=301]
-RewriteRule ^remote-doc-0006-in.jsonld$ tests/remote-doc-0001-in.jsonld [R=303]
-RewriteRule ^remote-doc-0007-in.jsonld$ tests/remote-doc-0001-in.jsonld [R=307]
-
-# Tests 0009-0011 Add link header
-<FilesMatch "remote-doc-0009-in.jsonld">
-  Header set Link '<remote-doc-0009-context.jsonld>; rel="http://www.w3.org/ns/json-ld#context"'
-</FilesMatch>
-<FilesMatch "remote-doc-0010-in.json">
-  Header set Link '<remote-doc-0010-context.jsonld>; rel="http://www.w3.org/ns/json-ld#context"'
-</FilesMatch>
-<FilesMatch "remote-doc-0011-in.jldt">
-  Header set Link '<remote-doc-0011-context.jsonld>; rel="http://www.w3.org/ns/json-ld#context"'
-</FilesMatch>
-
-# Test 00012 adds multiple link headers
-<FilesMatch "remote-doc-0012-in.json">
-  Header set Link '<remote-doc-0012-context1.jsonld>; rel="http://www.w3.org/ns/json-ld#context"'
-  Header append Link '<remote-doc-0012-context2.jsonld>; rel="http://www.w3.org/ns/json-ld#context"'
-</FilesMatch>
diff --git a/utils/.htaccess b/utils/.htaccess
deleted file mode 100644
index d5d0d53bd..000000000
--- a/utils/.htaccess
+++ /dev/null
@@ -1,5 +0,0 @@
-<Files remote-update-token.txt>
-  order allow,deny
-  deny from all
-</Files>
-
diff --git a/utils/README b/utils/README
deleted file mode 100644
index 8bb70691c..000000000
--- a/utils/README
+++ /dev/null
@@ -1,13 +0,0 @@
-Usage
------
-
-In order to use the git.php file, you must create a file called
-remote-update-token.txt and place a value in there. You must then call
-the git.php file with a URL parameter named 'token' set to the value in
-the file. For example:
-
-https://json-ld.org/utils/git.php?token=7384724849
-
-While this is not a fool-proof security solution, it'll be good enough
-for now. Updates are throttled, even in the event of a DDoS, the update
-rate is once every 5 seconds.
diff --git a/utils/git.php b/utils/git.php
deleted file mode 100644
index 97296748b..000000000
--- a/utils/git.php
+++ /dev/null
@@ -1,74 +0,0 @@
-<?php
-
-// Support < 5.4.0
-// https://stackoverflow.com/questions/3258634/php-how-to-send-http-response-code
-if(!function_exists('http_response_code'))
-{
-   function http_response_code($newcode = NULL)
-   {
-      static $code = 200;
-      if($newcode !== NULL)
-      {
-	 header('X-PHP-Response-Code: '.$newcode, true, $newcode);
-	 if(!headers_sent())
-	    $code = $newcode;
-      }
-      return $code;
-   }
-}
-
-// open the token file
-$tfile = fopen('remote-update-token.txt', 'r');
-
-// Acquire the lock in a non-blocking manner
-if($tfile == FALSE)
-{
-   http_response_code(400);
-   header('Content-Type: text/plain');
-   echo 'ERROR: You must create a file called remote-update-token.txt and ' .
-      'place a secret token in that file. See the README for more information.';
-}
-else if(flock($tfile, LOCK_EX | LOCK_NB))
-{
-   $token = trim(fgets($tfile));
-
-   // check to make sure that the token value is correct
-   if(array_key_exists('token', $_GET) and $token === $_GET['token'])
-   {
-      // perform a git pull
-      chdir('..');
-      $gitdir = getcwd() . "/.git";
-      $last_line = system("git --git-dir $gitdir pull", $retval);
-      if($retval !== 0) {
-         http_response_code(400);
-      }
-
-      header('Content-Type: text/plain');
-      echo 'git pull retval: ' . $retval . "\n";
-      echo 'git pull last line: ' . $last_line . "\n";
-      echo 'git pull complete';
-
-      // Sleep for 5 seconds to throttle the update rate to 12 per minute
-      sleep(5);
-   }
-   else
-   {
-      http_response_code(400);
-      header('Content-Type: text/plain');
-      echo 'ERROR: Invalid secret token provided. ' .
-         'See the README for more information.';
-   }
-
-   // Release the lock file
-   flock($tfile, LOCK_UN); // release the lock
-}
-else
-{
-   http_response_code(400);
-   header('Content-Type: text/plain');
-   echo 'ERROR: An update is currently being performed, ' .
-      'this request has been rejected.';
-}
-
-fclose($tfile);
-?>