URL 的[網路標準](https://www.w3.org/TR/WD-html40-970708/htmlweb.html)是有區分大小寫的,畢竟目前為止約有 80% 的網站都是使用 Unix-like 的作業系統架設的,而 Unix-like 作業系統中,是會區分大小寫的。那麼我們要如何讓架設在 GitHub Pages 的 Jekyll 網站,能支援不區分大小寫的網址呢?

Jekyll 本身沒有支援相關的設定,但是你可以透過 jekyll-redirect-from 套件來處理這個問題。

但這個方式有點麻煩,要在每個頁面都加上相關設定。

透過 GitHub Pages 的 404.html 機制,好像挺不錯的。

只要在你的網站中提供 404.html 檔案,當 GitHub Pages 找不到相關的網址(URL 大寫的網址)時,就會自動導向到 404.html 這個檔案。然後我們在這裡去處理相關的路由導向即可。

以下程式碼是以本部落格為例,使用 site.posts 取得所有網頁的 URL,然後和當前的網址進行比對,找到正確的網址做導向。

var allPosts = [];
function redirectToCorrectPage() {
    console.log('Unable to find page. Trying other URL cases.');
    
        allPosts.push('/serialize-json-object-without-third-party-library/');
    
        allPosts.push('/mention-someone-in-teams-webhook/');
    
        allPosts.push('/use-adaptive-cards-to-send-message-to-teams-webhook/');
    
        allPosts.push('/use-qr-barcode-to-send-message-and-something-eles/');
    
        allPosts.push('/make-jekyll-github-pages-case-insensitive/');
    
        allPosts.push('/output-csproj-variable-value-from-msbuild/');
    
        allPosts.push('/the-programmers-oath/');
    
        allPosts.push('/azure-devops-pipelines-logging-commands-color/');
    
        allPosts.push('/how-to-manually-download-and-install-windows-update/');
    
        allPosts.push('/develop-nuget-package-for-multiple-target-framework/');
    
        allPosts.push('/visual-studio-website-is-redirecting-http-to-https-when-debugging/');
    
        allPosts.push('/find-install-script-for-manual-install-azure-cli-extension/');
    
        allPosts.push('/npm-unable-get-local-issuer-certificate-behind-zscaler/');
    
        allPosts.push('/install-azure-devops-cli-behind-zscaler/');
    
        allPosts.push('/publish-blazor-webassembly-app-to-github-page/');
    
        allPosts.push('/set-dotnet-notebook-display-record-limit/');
    
        allPosts.push('/manually-download-multiple-versions-of-xcode/');
    
        allPosts.push('/npm-install-with-legacy-peer-deps/');
    
        allPosts.push('/passing-project-properties-to-sonarqube-scanner/');
    
        allPosts.push('/useing-powershell-to-update-sonarqube-project-visibility/');
    
        allPosts.push('/custom-a-magic-command-for-dotnet-notebook/');
    
        allPosts.push('/how-sonarqube-detect-duplication/');
    
        allPosts.push('/csharp-9-check-for-null-or-not/');
    
        allPosts.push('/run-program-as-different-user-in-windows/');
    
        allPosts.push('/vsto-introduction/');
    
        allPosts.push('/how-to-use-env-file-in-dotnet-interactive-notebook-load-env-file/');
    
        allPosts.push('/code-quality-with-ndepend/');
    
        allPosts.push('/windows-terminal-use-startupActions-to-default-open-ntop-on-new-tab/');
    
        allPosts.push('/export-flow-action-with-power-automate-desktop/');
    
        allPosts.push('/c-sharp-unit-testing-with-internal-access-modifier/');
    
        allPosts.push('/sonarlint-with-sonarqube/');
    
        allPosts.push('/microsoft-certifications-microsoft-exam-duration-and-question-types/');
    
        allPosts.push('/asp-net-core-reverse-proxy-with-different-virtual-root/');
    
        allPosts.push('/get-powershell-version/');
    
        allPosts.push('/play-with-dotnet-interactive-notebook/');
    
        allPosts.push('/typescript-running-typescript-ts-node/');
    
        allPosts.push('/quokkajs-import-npm-module/');
    
        allPosts.push('/develop-ios-app-no-mac-required/');
    
        allPosts.push('/json-serialize-with-interface-derived-property/');
    
        allPosts.push('/use-auto-hot-key-in-specific-program/');
    
        allPosts.push('/powershell-replace-file-content-function/');
    
        allPosts.push('/edit-default-dark-theme-for-visual-studio-code/');
    
        allPosts.push('/force-enable-edge-and-chrome-password-manager/');
    
        allPosts.push('/using-dynamic-type-to-bind-data-in-asp-net-core/');
    
        allPosts.push('/using-newtonsoft-json-in-asp-net-core-projects/');
    
        allPosts.push('/reinstall-nuget-package-to-upgrade-the-framework-version-of-all-projects/');
    
        allPosts.push('/css-isolation-in-blazor/');
    
        allPosts.push('/diff-an-image-using-imagemagick/');
    
        allPosts.push('/how-to-run-external-exe-file-in-dotnet/');
    
        allPosts.push('/dynamic-create-function-in-powershell/');
    
        allPosts.push('/setting-azure-web-app-to-https-with-free-ssl-service/');
    
        allPosts.push('/using-powershell-call-http-request/');
    
        allPosts.push('/drawing-azure-architecture-in-vscode-with-draw-io/');
    
        allPosts.push('/invoke-webrequest-response-cannot-be-parsed-because-ie-engine-is-not-available/');
    
        allPosts.push('/reducing-size-of-dotnet-app/');
    
        allPosts.push('/registering-and-open-an-app-with-custom-uri-scheme/');
    
        allPosts.push('/output-information-in-powershell/');
    
        allPosts.push('/using-exception-messages-with-try-catch-in-powershell/');
    
        allPosts.push('/build-deno-web-app-with-oak/');
    
        allPosts.push('/microsoft-teams-shortcut-hotkey/');
    
        allPosts.push('/build-aiot-solution-on-cloud-era/');
    
        allPosts.push('/use-powershell-requires-statement-to-restrict-running-requirement/');
    
        allPosts.push('/create-and-publish-powershell-module-to-powershell-gallery/');
    
        allPosts.push('/create-simple-javascipt-router-under-100-lines/');
    
        allPosts.push('/how-to-use-github-pages-build-a-short-url-app/');
    
        allPosts.push('/powershell-module-types-you-should-know/');
    
        allPosts.push('/use-powershell-to-automatic-pull-git-project/');
    
        allPosts.push('/import-powershell-script-with-import-module/');
    
        allPosts.push('/git-update-to-latest-version/');
    
        allPosts.push('/using-topshelf-and-dotnet-generic-host-build-windows-service/');
    
        allPosts.push('/pass-arguments-to-windows-service-with-topshelf/');
    
        allPosts.push('/run-windows-terminal-as-administrator-with-elevated-admin-permissions/');
    
        allPosts.push('/batch-uninstall-older-dotnet-sdk-version/');
    
        allPosts.push('/setting-powershell-theme-with-oh-my-posh/');
    
        allPosts.push('/system-text-json-custom-jsonconverter-for-timespan/');
    
        allPosts.push('/system-text-json-use-case-honor-serializer/');
    
        allPosts.push('/intellisense-in-chineses-for-msbuild-project-file-csproj/');
    
        allPosts.push('/az-exam-register-process/');
    
        allPosts.push('/show-code-summary-in-nuget-package/');
    
        allPosts.push('/how-to-prepare-microsoft-az-900-exam/');
    
        allPosts.push('/office-word-excel-powerpoint-online-viewer/');
    
        allPosts.push('/continuous-integration-with-latest-nuget-package-in-dotnet-framework-project/');
    
        allPosts.push('/formating-docker-ps-output/');
    
        allPosts.push('/customize-model-validation-response-in-net-core-webapi/');
    
        allPosts.push('/using-microsoft-learn-as-company-training-center/');
    
        allPosts.push('/thanks-for-you-2019/');
    
        allPosts.push('/speeding-up-sourcetree-fetching-older-commits/');
    
        allPosts.push('/make-your-powershell-handy/');
    
        allPosts.push('/using-postman-fetch-and-set-environment-variables/');
    
        allPosts.push('/transcribe-speech-to-text-with-azure-cognitive-speech-service-and-dotnet-core/');
    
        allPosts.push('/list-azure-service-regions/');
    
        allPosts.push('/visual-studio-launch-settings-iis-express-iis-project-executable/');
    
        allPosts.push('/enable-ie-mode-microsoft-edge/');
    
        allPosts.push('/asp-net-core-web-api-global-exception-handler/');
    
        allPosts.push('/logging-http-request-in-asp-net-core/');
    
        allPosts.push('/using-json-serializer-with-system-text-json/');
    
        allPosts.push('/windows-and-linux-has-different-newline-characters/');
    
        allPosts.push('/dotnet-core-global-exception-handler-in-console-application/');
    
        allPosts.push('/create-microsoft-teams-app/');
    
        allPosts.push('/how-to-debug-inside-microsoft-teams/');
    
        allPosts.push('/how-to-build-early-version-of-new-windows-terminal/');
    
        allPosts.push('/enumerate-depth-directories-or-subfolders-path/');
    
        allPosts.push('/all-version-of-dotnet-framework-runtime-download-link/');
    
        allPosts.push('/chrome-extension-source-code-viewer/');
    
        allPosts.push('/microsoft-edge-install-chrome-web-store-extensions/');
    
        allPosts.push('/sonarqube-no-analysable-projects-were-found/');
    
        allPosts.push('/authenticating-jwt-tokens-in-asp-net-core-webapi/');
    
        allPosts.push('/vue-cli-with-dotnet-core-api/');
    
        allPosts.push('/create-template-console-app-project-in-visual-studio/');
    
        allPosts.push('/autofill-tscc-boarding-info-query-paramter/');
    
        allPosts.push('/modify-program-name-on-db-connection-session-and-use-linq-to-db-on-linqpad/');
    
        allPosts.push('/build-and-output-references-to-dotnet-core-projects/');
    
        allPosts.push('/working-with-json-in-razor-pages-like-web-api/');
    
        allPosts.push('/explore-full-set-of-wmi-class-and-properties-with-wmi-explorer/');
    
        allPosts.push('/output-static-content-file-to-project-in-dotnet-core-nuget-pack/');
    
        allPosts.push('/how-to-check-dotnet-core-iis-hosting-bundle-version/');
    
        allPosts.push('/using-csharp-code-in-powershell-scripts/');
    
        allPosts.push('/publish-angular-app-to-github-pages/');
    
        allPosts.push('/preview-svg-thumbnails-in-in-windows-explorer/');
    
        allPosts.push('/dotnet-core-console-app-with-dependency-injection/');
    
        allPosts.push('/application-insights-with-doenet-core-console/');
    
        allPosts.push('/dotnet-core-console-app-with-configuration/');
    
        allPosts.push('/use-doskey-to-alias-docker-command/');
    
        allPosts.push('/deserialize-enum-to-json-with-string/');
    
        allPosts.push('/how-to-config-sonarqube-work-with-vsts/');
    
        allPosts.push('/how-to-query-and-analytics-application-insights-log/');
    
        allPosts.push('/use-file-system-watcher-collect-iot-log/');
    
        allPosts.push('/ml-dotnet-release-0.3-cht/');
    
        allPosts.push('/build-bot-framework-with-bot-builder-v4/');
    
        allPosts.push('/developing-wpf-or-uwp-with-material-desing/');
    
        allPosts.push('/asp-net-core-windows-authentication/');
    
        allPosts.push('/ml-dotnet-release-0.2-cht/');
    
        allPosts.push('/docker-cheat-sheet-in-traditional-chinese/');
    
        allPosts.push('/use-multiple-configurations-with-wpf/');
    
        allPosts.push('/ml-dotnet-release-0.1-cht/');
    
        allPosts.push('/introduction-azure-bot-service-and-bot-framework/');
    
        allPosts.push('/npm-install-error-could-not-load-the-visual-c-component-vcbuild/');
    
        allPosts.push('/deploy-exe-application-without-installing/');
    
        allPosts.push('/thank-you-note-2018-mvp/');
    
        allPosts.push('/export-provisioning-profile-with-free-ios-developer/');
    
        allPosts.push('/vsts-deploy-multiple-azure-websites/');
    
        allPosts.push('/vsts-deploy-azure-web-app-occur-error-file-in-use/');
    
        allPosts.push('/import-rxjs-correctly/');
    
        allPosts.push('/retrieve-data-annotations-from-model/');
    
        allPosts.push('/gitignore-and-delete-untracked-files/');
    
        allPosts.push('/scrum-guides/');
    
        allPosts.push('/linq-to-xml-extract-data-from-cdata/');
    
        allPosts.push('/linq-to-xml-validate-xml/');
    
        allPosts.push('/linq-to-xml-transfom-xml/');
    
        allPosts.push('/linq-to-xml-edit-xml/');
    
        allPosts.push('/linq-to-xml-query-xml/');
    
        allPosts.push('/linq-to-xml-create-xml-file/');
    
        allPosts.push('/linq-to-xml-basic-usage/');
    
        allPosts.push('/create-nuget-package/');
    
        allPosts.push('/hosting-your-own-nuget-server/');
    
        allPosts.push('/use-the-latest-c-sharp-compiler/');
    
        allPosts.push('/use-soapui-to-test-web-service/');
    
        allPosts.push('/angular-with-google-analytics-by-global-site-tag-aka-gtag-js/');
    
        allPosts.push('/angular-flex-layout-cheat-sheet/');
    
        allPosts.push('/execute-http-call-when-angular-initialize/');
    
        allPosts.push('/use-forroot-to-provide-services-in-angular-shared-module/');
    
        allPosts.push('/use-angular-flex-layout-package/');
    
        allPosts.push('/how-to-use-html-head/');
    
        allPosts.push('/disable-browser-cache-on-angular-site/');
    
        allPosts.push('/microsoft-user-secret-manager-with-dotnet-core/');
    
        allPosts.push('/bypass-browser-security-warning-with-pseudo-password/');
    
        allPosts.push('/how-to-use-bowser-sync/');
    
        allPosts.push('/use-thired-party-package-to-implement-ldap-authenticate-in-dotnet-core/');
    
        allPosts.push('/ldap-introduction/');
    
        allPosts.push('/use-ngrok-demo-local-development/');
    
        allPosts.push('/visual-studio-spell-checker/');
    
        allPosts.push('/asp-net-core-webapi-with-entity-framework-core/');
    
        allPosts.push('/caching-result-with-angular-http-service/');
    
        allPosts.push('/click-button-copy-to-clipboard-in-angular/');
    
        allPosts.push('/guide-to-use-github-flow/');
    
        allPosts.push('/my-vscode-config/');
    
        allPosts.push('/how-to-use-clang-format-in-vscode/');
    
        allPosts.push('/cordova-with-wikitude-vr-sdk/');
    
        allPosts.push('/visible-and-hidden-in-bootstrap-4/');
    
        allPosts.push('/troubleshoot-an-error-occurred-in-asp-dot-net-core-on-iis/');
    
        allPosts.push('/wallaby-js-advanced-logging/');
    
        allPosts.push('/integrate-swiper-into-angular/');
    
        allPosts.push('/find-the-max-value-in-an-array-of-objects/');
    
        allPosts.push('/refresh-ssms-intellisense-cache-to-update-scheme/');
    
        allPosts.push('/use-ionic-cli-3-build-ionic-1-project/');
    
        allPosts.push('/angualr-cli-with-dotnet-core/');
    
        allPosts.push('/total-clean-uninstall-visual-studio/');
    
        allPosts.push('/deploy-angular-to-iis-virtual-directory/');
    
        allPosts.push('/angular-rxjs-operaror-location/');
    
        allPosts.push('/build-vscode-extension/');
    
        allPosts.push('/publish-extension-to-visual-studio-marketplace/');
    
        allPosts.push('/web-config-mime/');
    
        allPosts.push('/deploy-angular-to-azure-app-service/');
    
        allPosts.push('/generating-sitemap-in-jekyll-without-plugin/');
    
        allPosts.push('/line-notify-2-use-web-api/');
    
        allPosts.push('/small-virtual-keyboard-for-chinese/');
    
        allPosts.push('/iis-multi-domain-ssl/');
    
        allPosts.push('/line-notify-1-basic/');
    
        allPosts.push('/http-status-code/');
    
        allPosts.push('/use-hybrid-connection-connect-azure-vm-and-on-premises/');
    
        allPosts.push('/sql-server-table-field-description/');
    
        allPosts.push('/filter-the-results-of-a-stored-procedure/');
    
        allPosts.push('/enable-dark-theme-in-sql-server-management-studio-2016/');
    
        allPosts.push('/iis-http-header-ie-compatible/');
    
        allPosts.push('/jekyll-facebook-like-button/');
    
        allPosts.push('/angular-mime-mapping/');
    
        allPosts.push('/create-large-empty-file/');
    
        allPosts.push('/crosswalk-cordova/');
    
        allPosts.push('/vs-code-github-ssh-push/');
    
        allPosts.push('/package-json-version/');
    
        allPosts.push('/net-core-drop-package-json/');
    
        allPosts.push('/angular-vscode-extensions/');
    
        allPosts.push('/oracle-client-windows/');
    
        allPosts.push('/android-accept-agreements/');
    
        allPosts.push('/sonarqube-csharp/');
    
        allPosts.push('/vscode-shortcuts/');
    
        allPosts.push('/yarn/');
    
        allPosts.push('/onenote-code-highlight/');
    
        allPosts.push('/linq-cheat-sheet/');
    
        allPosts.push('/sql-openquery-8000-limit/');
    
        allPosts.push('/install-adventureworks-sql-database/');
    
        allPosts.push('/cordova-camera-authorization/');
    
        allPosts.push('/powershell-alias/');
    
        allPosts.push('/download-gist/');
    
        allPosts.push('/sql-2000-dblink/');
    
        allPosts.push('/azure-hybrid-connections/');
    
        allPosts.push('/linqpad-sql-command-class-model/');
    
        allPosts.push('/gitlab-backup/');
    
        allPosts.push('/useful-hacks-for-javascript/');
    
        allPosts.push('/oracle-client-server-interoperability/');
    
        allPosts.push('/cordova-ios-localized-app-name/');
    
        allPosts.push('/cordova-android-localized-app-name/');
    
        allPosts.push('/javascript-copy-clipboard/');
    
        allPosts.push('/android-app-google-play/');
    
        allPosts.push('/oracle-dataaccess/');
    
        allPosts.push('/cordova-auto-increment-version-number/');
    
        allPosts.push('/cordova-jshint/');
    
        allPosts.push('/cordova-uglify/');
    
        allPosts.push('/using-jsondotnet-in-linqpad/');
    
        allPosts.push('/improving-angular-performance-for-production/');
    
        allPosts.push('/windows-nvm-install/');
    
        allPosts.push('/remotebuild-hook-failed-with-error-code-127/');
    
        allPosts.push('/batch-rename/');
    
        allPosts.push('/save-image-to-camera-roll-in-cordova/');
    
        allPosts.push('/webapi-return-specific-content-type/');
    
        allPosts.push('/git-how-to-remove-file-and-commit-from-history/');
    
        allPosts.push('/supported-github-flavored-markdown/');
    
        allPosts.push('/ftp-530-valid-hostname-is-expected/');
    
        allPosts.push('/visual-studio-code-release-and-configure-language/');
    
        allPosts.push('/ionic-update-package-json/');
    
        allPosts.push('/how-to-detect-user-device/');
    
        allPosts.push('/sql-injection-recover/');
    
        allPosts.push('/change-ubuntu-screen-resolution-host-on-virtualbox/');
    
        allPosts.push('/javascript-iframe/');
    
        allPosts.push('/sql-server-open-query/');
    
        allPosts.push('/oracle-sql-special-characters/');
    
        allPosts.push('/note-youtube-dl/');
    
        allPosts.push('/note-windows-terminal/');
    
        allPosts.push('/note-windows-hotkey/');
    
        allPosts.push('/note-vue/');
    
        allPosts.push('/note-visual-studio/');
    
        allPosts.push('/note-vi-vim/');
    
        allPosts.push('/note-unit-test/');
    
        allPosts.push('/note-travis-ci/');
    
        allPosts.push('/note-sqlite/');
    
        allPosts.push('/note-semiconductor-manufacturing/');
    
        allPosts.push('/note-selenium/');
    
        allPosts.push('/note-regular-expression/');
    
        allPosts.push('/note-refactoring/');
    
        allPosts.push('/note-redis/');
    
        allPosts.push('/note-rammap/');
    
        allPosts.push('/note-powershell/');
    
        allPosts.push('/note-plantuml/');
    
        allPosts.push('/note-oracle-sql/');
    
        allPosts.push('/note-node-js/');
    
        allPosts.push('/note-ms-sql/');
    
        allPosts.push('/note-misc/');
    
        allPosts.push('/note-mime-type/');
    
        allPosts.push('/note-machine-learning/');
    
        allPosts.push('/note-javascript/');
    
        allPosts.push('/note-iis-express/');
    
        allPosts.push('/note-git/');
    
        allPosts.push('/note-ffmpeg/');
    
        allPosts.push('/note-epub/');
    
        allPosts.push('/note-english-email/');
    
        allPosts.push('/note-dotnet/');
    
        allPosts.push('/note-dotnet-windows-app/');
    
        allPosts.push('/note-dotnet-il/');
    
        allPosts.push('/note-docker/');
    
        allPosts.push('/note-dapper/');
    
        allPosts.push('/note-css/');
    
        allPosts.push('/note-command-line/');
    
        allPosts.push('/note-cmder/');
    
        allPosts.push('/note-character-pronunciation/');
    
        allPosts.push('/note-browser-extension/');
    
        allPosts.push('/note-batch/');
    
        allPosts.push('/note-azure/');
    
        allPosts.push('/note-angular/');
    
        allPosts.push('/note-angular-cli/');
    
        allPosts.push('/note-WSL/');
    
        allPosts.push('/class-naming-convention/');
    
    var url = window.location.pathname;
    // strip trailing /
    if (url.slice(-1) === '/') {
        url = url.slice(0, -1);
    }
    var allPostsUpperCase = allPosts.map(function (value) {
        // strip trailing /
        if (value.slice(-1) === '/') {
            value = value.slice(0, -1);
        }
        return value.toUpperCase();
    });
    console.log('Looking for ' + url.toUpperCase() + ' in ' + allPostsUpperCase);
    var i = allPostsUpperCase.indexOf(url.toUpperCase());
    if (i != -1) {
        console.log(allPosts[i]);
        window.location = allPosts[i];
    }
}
window.onload = redirectToCorrectPage;

這樣就可以讓 GitHub Pages 支援不區分大小寫的網址了。


參考資料:


Poy Chang

Trial and Error