diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp
index 5d02865f4..3e667e74a 100644
--- a/src/core/file_sys/card_image.cpp
+++ b/src/core/file_sys/card_image.cpp
@@ -31,13 +31,9 @@ XCI::XCI(VirtualFile file_, u64 program_id, size_t program_index)
     : file(std::move(file_)), program_nca_status{Loader::ResultStatus::ErrorXCIMissingProgramNCA},
       partitions(partition_names.size()),
       partitions_raw(partition_names.size()), keys{Core::Crypto::KeyManager::Instance()} {
-    if (file->ReadObject(&header) != sizeof(GamecardHeader)) {
-        status = Loader::ResultStatus::ErrorBadXCIHeader;
-        return;
-    }
-
-    if (header.magic != Common::MakeMagic('H', 'E', 'A', 'D')) {
-        status = Loader::ResultStatus::ErrorBadXCIHeader;
+    const auto header_status = TryReadHeader();
+    if (header_status != Loader::ResultStatus::Success) {
+        status = header_status;
         return;
     }
 
@@ -316,6 +312,44 @@ Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) {
     return Loader::ResultStatus::Success;
 }
 
+Loader::ResultStatus XCI::TryReadHeader() {
+    constexpr size_t CardInitialDataRegionSize = 0x1000;
+
+    // Define the function we'll use to determine if we read a valid header.
+    const auto ReadCardHeader = [&]() {
+        // Ensure we can read the entire header. If we can't, we can't read the card image.
+        if (file->ReadObject(&header) != sizeof(GamecardHeader)) {
+            return Loader::ResultStatus::ErrorBadXCIHeader;
+        }
+
+        // Ensure the header magic matches. If it doesn't, this isn't a card image header.
+        if (header.magic != Common::MakeMagic('H', 'E', 'A', 'D')) {
+            return Loader::ResultStatus::ErrorBadXCIHeader;
+        }
+
+        // We read a card image header.
+        return Loader::ResultStatus::Success;
+    };
+
+    // Try to read the header directly.
+    if (ReadCardHeader() == Loader::ResultStatus::Success) {
+        return Loader::ResultStatus::Success;
+    }
+
+    // Get the size of the file.
+    const size_t card_image_size = file->GetSize();
+
+    // If we are large enough to have a key area, offset past the key area and retry.
+    if (card_image_size >= CardInitialDataRegionSize) {
+        file = std::make_shared<OffsetVfsFile>(file, card_image_size - CardInitialDataRegionSize,
+                                               CardInitialDataRegionSize);
+        return ReadCardHeader();
+    }
+
+    // We had no header and aren't large enough to have a key area, so this can't be parsed.
+    return Loader::ResultStatus::ErrorBadXCIHeader;
+}
+
 u8 XCI::GetFormatVersion() {
     return GetLogoPartition() == nullptr ? 0x1 : 0x2;
 }
diff --git a/src/core/file_sys/card_image.h b/src/core/file_sys/card_image.h
index 1283f8216..9886123e7 100644
--- a/src/core/file_sys/card_image.h
+++ b/src/core/file_sys/card_image.h
@@ -128,6 +128,7 @@ public:
 
 private:
     Loader::ResultStatus AddNCAFromPartition(XCIPartition part);
+    Loader::ResultStatus TryReadHeader();
 
     VirtualFile file;
     GamecardHeader header{};