diff --git a/Build/Scripts/cglFixMyCommitFileHeader.bat b/Build/Scripts/cglFixMyCommitFileHeader.bat
new file mode 100644
index 0000000000000000000000000000000000000000..8c4f96ddd0949929ac4e4007898e5e406ff2a674
--- /dev/null
+++ b/Build/Scripts/cglFixMyCommitFileHeader.bat
@@ -0,0 +1,4 @@
+@ECHO OFF
+FOR /F %%i in ('git diff-tree --no-commit-id --name-only -r HEAD ^| findstr -i ".*\.php"') DO (
+	bin\php-cs-fixer.bat fix --path-mode intersection --config=Build/php-cs-fixer/header-comment.php %%i
+)
diff --git a/Build/Scripts/cglFixMyCommitFileHeader.sh b/Build/Scripts/cglFixMyCommitFileHeader.sh
new file mode 100755
index 0000000000000000000000000000000000000000..a637b6af542f99b45a719ab8f9b44e743b262dc7
--- /dev/null
+++ b/Build/Scripts/cglFixMyCommitFileHeader.sh
@@ -0,0 +1,145 @@
+#!/usr/bin/env bash
+
+#########################
+#
+# CGL fix.
+#
+# It expects to be run from the core root.
+#
+# To auto-fix single files, use the php-cs-fixer command directly
+# substitute $FILE with a filename
+#
+##########################
+
+# --------------------------
+# --- default parameters ---
+# --------------------------
+# check files in last commit
+filestype=commit
+# non-dryrun is default
+DRYRUN=""
+DRYRUN_OPTIONS="--dry-run --diff"
+
+# ----------------------
+# --- automatic vars ---
+# ----------------------
+progname=$(basename $0)
+
+# ------------------------
+# --- print usage info ---
+# ------------------------
+usage()
+{
+    echo "Usage: $0 [options]                                      "
+    echo " "
+    echo "no arguments/default: fix all php files in last commit   "
+    echo " "
+    echo "Options:                                                 "
+    echo " -f <commit|cache|stdin>                                 "
+    echo "      specifies which files to check:                    "
+    echo "      - commit (default): all files in latest commit     "
+    echo "      - cache : all files in git cache (staging area)    "
+    echo "      - stdin : read list of files from stdin            "
+    echo " "
+    echo " -n                                                      "
+    echo "      dryrun only, do not fix anything!                  "
+    echo " "
+    echo " -h                                                      "
+    echo "      help                                               "
+    echo " "
+    echo "Note: In order to still support command line options of  "
+    echo " older versions of this script, you can use the argument "
+    echo " dryrun.                                                 "
+    echo " "
+    echo " THIS IS NOT RECOMMENDED but will still work for now     "
+    echo " Usage: $0 [options] [dryrun]                            "
+    exit 0
+}
+
+# -----------------------
+# --- parsing of args ---
+# -----------------------
+OPTIND=1
+
+while getopts "hnf:" opt;do
+    case "$opt" in
+    h)
+        usage
+        ;;
+    f)
+        filestype=$OPTARG
+        echo "$0 files type=$filestype"
+        ;;
+    n)
+        echo "$progname: dryrun mode"
+        DRYRUN="$DRYRUN_OPTIONS"
+        ;;
+    esac
+done
+
+shift $((OPTIND-1))
+
+if [ "$1" = "dryrun" ]
+then
+    echo "$progname: dryrun mode"
+    DRYRUN="$DRYRUN_OPTIONS"
+fi
+
+# --------------------------------------
+# --- check if php executable exists ---
+# --------------------------------------
+exist_php_executable() {
+    which php >/dev/null 2>/dev/null
+    if [ $? -ne 0 ];then
+        echo "$progname: No php executable found\n"
+        exit 1
+    fi
+}
+
+
+# ------------------------------
+# --- run php without xdebug ---
+# ------------------------------
+php_no_xdebug ()
+{
+    temporaryPath="$(mktemp -t php.XXXXXX).ini"
+    php -i | grep "\.ini" | grep -o -e '\(/[A-Za-z0-9._-]\+\)\+\.ini' | grep -v xdebug | xargs awk 'FNR==1{print ""}1' > "${temporaryPath}"
+    php -n -c "${temporaryPath}" "$@"
+    RETURN=$?
+    rm -f "${temporaryPath}"
+    exit $RETURN
+}
+
+# ------------------------------------
+# --- get a list of files to check ---
+# ------------------------------------
+if [[ $filestype == commit ]];then
+    echo "$progname: Searching for php files in latest git commit ..."
+    DETECTED_FILES=`git diff-tree --no-commit-id --name-only -r HEAD | grep '.php$' 2>/dev/null`
+elif [[ $filestype == cache ]];then
+    echo "$progname: Searching for php files in git cache ..."
+    DETECTED_FILES=`git diff --cached --name-only | grep '.php$' 2>/dev/null`
+elif [[ $filestype == stdin ]];then
+    echo "$progname: reading list of php files to check from stdin"
+    DETECTED_FILES=$(cat)
+else
+    echo "$progname: ERROR: unknown filetype, possibly used -f with wrong argument"
+    usage
+fi
+if [ -z "${DETECTED_FILES}" ]
+then
+    echo "$progname: No PHP files to check, all is well."
+    exit 0
+fi
+
+# ---------------------------------
+# --- run php-cs-fixer on files ---
+# ---------------------------------
+exist_php_executable
+php_no_xdebug ./bin/php-cs-fixer fix \
+    -v ${DRYRUN} \
+    --path-mode intersection \
+    --config=Build/php-cs-fixer/header-comment.php \
+    `echo ${DETECTED_FILES} | xargs ls -d 2>/dev/null`
+
+exit $?
diff --git a/Build/Scripts/runTests.sh b/Build/Scripts/runTests.sh
index 5a0b0e2cbd600fe42facedbb05201b3d4fc78607..a4e9cbfc052f6b428d440ff6f256ab856f75e9bc 100755
--- a/Build/Scripts/runTests.sh
+++ b/Build/Scripts/runTests.sh
@@ -135,6 +135,8 @@ Options:
             - buildJavascript: execute typescript to javascript builder
             - cgl: test and fix all core php files
             - cglGit: test and fix latest committed patch for CGL compliance
+            - cglHeader: test and fix file header for all core php files
+            - cglHeaderGit: test and fix latest committed patch for CGL file header compliance
             - checkAnnotations: check php code for allowed annotations
             - checkBom: check UTF-8 files do not contain BOM
             - checkComposer: check composer.json files for version integrity
@@ -246,7 +248,7 @@ Options:
         replay the unit tests in that order.
 
     -n
-        Only with -s cgl|cglGit
+        Only with -s cgl|cglGit|cglHeader|cglGitHeader
         Activate dry-run in CGL check that does not actively change files and only prints broken ones.
 
     -u
@@ -559,6 +561,22 @@ case ${TEST_SUITE} in
         SUITE_EXIT_CODE=$?
         docker-compose down
         ;;
+    cglHeader)
+        # Active dry-run for cgl needs not "-n" but specific options
+        if [ -n "${CGLCHECK_DRY_RUN}" ]; then
+            CGLCHECK_DRY_RUN="--dry-run --diff"
+        fi
+        setUpDockerComposeDotEnv
+        docker-compose run cgl_header_all
+        SUITE_EXIT_CODE=$?
+        docker-compose down
+        ;;
+    cglHeaderGit)
+        setUpDockerComposeDotEnv
+        docker-compose run cgl_header_git
+        SUITE_EXIT_CODE=$?
+        docker-compose down
+        ;;
     checkAnnotations)
         setUpDockerComposeDotEnv
         docker-compose run check_annotations
diff --git a/Build/git-hooks/unix+mac/pre-commit b/Build/git-hooks/unix+mac/pre-commit
index eb964220f8b5a6ee1ac1087c7b3b97535c4a9c75..f8751b85b6ae463c5697a6c6c440ac9509326226 100755
--- a/Build/git-hooks/unix+mac/pre-commit
+++ b/Build/git-hooks/unix+mac/pre-commit
@@ -1,26 +1,34 @@
 #!/usr/bin/env bash
 
 # pre-commit hook
-# - check if staged files adhere to Coding Guidelines
+# - check if staged files adhere to Coding Guidelines and have proper file header
 # - abort on error:no for now, override with environment variable TYPO3_GIT_HOOK_ABORT_ON_ERROR
 #
 # Dependencies:
 #   uses: Build/Scripts/cglFixMyCommit.sh
 #
 
+USE_EXIT_CODE=0
 script=Build/Scripts/cglFixMyCommit.sh
+scriptFileHeader=Build/Scripts/cglFixMyCommitFileHeader.sh
 
 ABORT_ON_ERROR="no"
 ERROR_TEXT="\n"
 ERROR_CODE=0
+ERROR_CODE_FILE_HEADER=0
 
 if [ -z "${TYPO3_GIT_HOOK_ABORT_ON_ERROR+x}" ]; then
     ABORT_ON_ERROR=${TYPO3_GIT_HOOK_ABORT_ON_ERROR}
 fi
 
 if [ ! -x $script ];then
-    "echo "$script does not exist or is not executable"
-    "exit 0
+    echo "$script does not exist or is not executable"
+    exit 0
+fi
+
+if [ ! -x $scriptFileHeader ];then
+    echo "$scriptFileHeader does not exist or is not executable"
+    exit 0
 fi
 
 # call script with -f <cache> parameter: check files in git cache (staging area)
@@ -28,21 +36,42 @@ fi
 $script -f cache -n
 ERROR_CODE=$?
 
-
 if [ ${ERROR_CODE} -ne 0 ];then
-     echo -e "\n-----------------------------------------------------------------\n"
-     echo -e "  >> ERROR: There was a coding guideline problem in one or more of  "
-     echo -e "              your php files.                                       "
-     echo -e "   Please refer to [1] for details on the coding guidelines         "
-     echo -e "   Please refer to [2] for details on contribution                  "
-     echo -e "   [1] https://docs.typo3.org/typo3cms/CoreApiReference/CodingGuidelines/Index.html"
-     echo -e "   [2] https://docs.typo3.org/typo3cms/ContributionWorkflowGuide/   "
-     echo -e "------------------------------------------------------------------\n"
-     if [[ ${ABORT_ON_ERROR} == "yes" ]];then
-         echo -e "   Your commit is being aborted ... Fix and try again!          "
-         exit 1
-     else
-         echo -e "   You must fix this and then commit again (git commit --amend) "
-     fi
+    echo -e "\n-----------------------------------------------------------------\n"
+    echo -e "  >> ERROR: There was a coding guideline problem in one or more of  "
+    echo -e "              your php files.                                       "
+    echo -e "   Please refer to [1] for details on the coding guidelines         "
+    echo -e "   Please refer to [2] for details on contribution                  "
+    echo -e "   [1] https://docs.typo3.org/typo3cms/CoreApiReference/CodingGuidelines/Index.html"
+    echo -e "   [2] https://docs.typo3.org/typo3cms/ContributionWorkflowGuide/   "
+    echo -e "------------------------------------------------------------------\n"
+    if [[ ${ABORT_ON_ERROR} == "yes" ]];then
+        echo -e "   Your commit is being aborted ... Fix and try again!          "
+        USE_EXIT_CODE=1
+    else
+        echo -e "   You must fix this and then commit again (git commit --amend) "
+    fi
+fi
+
+$scriptFileHeader -f cache -n
+ERROR_CODE_FILE_HEADER=$?
+
+if [ ${ERROR_CODE_FILE_HEADER} -ne 0 ];then
+
+    echo -e "\n-----------------------------------------------------------------\n"
+    echo -e "  >> ERROR: There was a missing or wrong php file header in one or  "
+    echo -e "               more of your php files.                              "
+    echo -e "   Please refer to [1] for details on the coding guidelines         "
+    echo -e "   Please refer to [2] for details on contribution                  "
+    echo -e "   [1] https://docs.typo3.org/typo3cms/CoreApiReference/CodingGuidelines/Index.html"
+    echo -e "   [2] https://docs.typo3.org/typo3cms/ContributionWorkflowGuide/   "
+    echo -e "------------------------------------------------------------------\n"
+    if [[ ${ABORT_ON_ERROR} == "yes" ]];then
+        echo -e "   Your commit is being aborted ... Fix and try again!          "
+        USE_EXIT_CODE=1
+    else
+        echo -e "   You must fix this and then commit again (git commit --amend) "
+    fi
 fi
-exit 0
+
+exit ${USE_EXIT_CODE}
diff --git a/Build/gitlab-ci/nightly/integrity.yml b/Build/gitlab-ci/nightly/integrity.yml
index 5592c03d4d105657cdb86f393f71c0770f087383..b43c2f2c2e5e4a5ebd57937ebe13e14525f0dbf1 100644
--- a/Build/gitlab-ci/nightly/integrity.yml
+++ b/Build/gitlab-ci/nightly/integrity.yml
@@ -15,6 +15,7 @@ cgl:
   script:
     - Build/Scripts/runTests.sh -s composerInstall -p 8.1
     - Build/Scripts/runTests.sh -s cgl -n -p 8.1
+    - Build/Scripts/runTests.sh -s cglHeader -n -p 8.1
 
 grunt clean:
   stage: integrity
diff --git a/Build/gitlab-ci/pre-merge/integrity.yml b/Build/gitlab-ci/pre-merge/integrity.yml
index 37b919990213068307ea803bc78e60ec93821986..5a380a9e79fb40479d4cc3987ed070a2f7ead2c7 100644
--- a/Build/gitlab-ci/pre-merge/integrity.yml
+++ b/Build/gitlab-ci/pre-merge/integrity.yml
@@ -17,6 +17,7 @@ cgl pre-merge:
   script:
     - Build/Scripts/runTests.sh -s composerInstall -p 8.1
     - Build/Scripts/runTests.sh -s cglGit -n -p 8.1
+    - Build/Scripts/runTests.sh -s cglHeaderGit -n -p 8.1
 
 grunt clean pre-merge:
   stage: main
diff --git a/Build/php-cs-fixer/header-comment.php b/Build/php-cs-fixer/header-comment.php
index 417b9859199aa55dd097f59e43e81c1e5a8bd34b..19e416c90bed6a9b680a827ef47e1262ad06e668 100644
--- a/Build/php-cs-fixer/header-comment.php
+++ b/Build/php-cs-fixer/header-comment.php
@@ -30,6 +30,10 @@ $finder = PhpCsFixer\Finder::create()
     ->notName('ext_localconf.php')
     ->notName('ext_tables.php')
     ->notName('ext_emconf.php')
+    // ClassAliasMap files do not need header comments
+    ->notName('ClassAliasMap.php')
+    // CodeSnippets and Examples in Documentation do not need header comments
+    ->exclude('Documentation')
     // Third-party inclusion files should not have a changed comment
     ->notName('Rfc822AddressesParser.php')
     ->notName('ClassMapGenerator.php')
@@ -48,9 +52,10 @@ LICENSE.txt file that was distributed with this source code.
 The TYPO3 project - inspiring people to share!
 COMMENT;
 
-return PhpCsFixer\Config::create()
+return (new \PhpCsFixer\Config())
     ->setRiskyAllowed(false)
     ->setRules([
+        'no_extra_blank_lines' => true,
         'header_comment' => [
             'header' => $headerComment,
             'comment_type' => 'comment',
diff --git a/Build/testing-docker/local/docker-compose.yml b/Build/testing-docker/local/docker-compose.yml
index 7e41126850b224a646198546becd1bb7c04c6160..9ac0628e453d3ebfb27c62c66211065c60a6b131 100644
--- a/Build/testing-docker/local/docker-compose.yml
+++ b/Build/testing-docker/local/docker-compose.yml
@@ -513,6 +513,35 @@ services:
           --config=Build/php-cs-fixer.php typo3/
       "
 
+  cgl_header_git:
+    image: typo3/core-testing-${DOCKER_PHP_IMAGE}:latest
+    user: "${HOST_UID}"
+    volumes:
+      - ${CORE_ROOT}:${CORE_ROOT}
+    working_dir: ${CORE_ROOT}
+    command: >
+      /bin/sh -c "
+        if [ ${SCRIPT_VERBOSE} -eq 1 ]; then
+          set -x
+        fi
+        Build/Scripts/cglFixMyCommitFileHeader.sh ${CGLCHECK_DRY_RUN};
+      "
+
+  cgl_header_all:
+    image: typo3/core-testing-${DOCKER_PHP_IMAGE}:latest
+    user: "${HOST_UID}"
+    volumes:
+      - ${CORE_ROOT}:${CORE_ROOT}
+    working_dir: ${CORE_ROOT}
+    command: >
+      /bin/sh -c "
+        if [ ${SCRIPT_VERBOSE} -eq 1 ]; then
+          set -x
+        fi
+        php -dxdebug.mode=off bin/php-cs-fixer fix -v ${CGLCHECK_DRY_RUN} --path-mode intersection \
+          --config=Build/php-cs-fixer/header-comment.php typo3/
+      "
+
   check_annotations:
     image: typo3/core-testing-${DOCKER_PHP_IMAGE}:latest
     user: "${HOST_UID}"