diff --git a/backend/src/utils/fileSecurityUtils.js b/backend/src/utils/fileSecurityUtils.js
index 39e59b70..28155b33 100644
--- a/backend/src/utils/fileSecurityUtils.js
+++ b/backend/src/utils/fileSecurityUtils.js
@@ -79,6 +79,22 @@ const ALLOWED_IMAGE_TYPES = {
extensions: ['.svg'],
// SVG files are XML-based text files, so we skip magic number validation
magicNumbers: null
+ },
+ // HEIC/HEIF (iPhone). ISO-BMFF container: bytes 4-7 are the "ftyp" box marker,
+ // present in every HEIF/HEIC file (single entry — the magic check is `.every`,
+ // so alternatives can't be listed as separate entries). Sharp's libvips
+ // decodes these; extension + MIME are already gated by validateFileType.
+ 'image/heic': {
+ extensions: ['.heic'],
+ magicNumbers: [
+ { offset: 4, bytes: [0x66, 0x74, 0x79, 0x70] } // "ftyp"
+ ]
+ },
+ 'image/heif': {
+ extensions: ['.heif'],
+ magicNumbers: [
+ { offset: 4, bytes: [0x66, 0x74, 0x79, 0x70] } // "ftyp"
+ ]
}
};
diff --git a/frontend/src/components/admin/PhotoUpload.tsx b/frontend/src/components/admin/PhotoUpload.tsx
index 7d3f0ea1..b3a7d0a1 100644
--- a/frontend/src/components/admin/PhotoUpload.tsx
+++ b/frontend/src/components/admin/PhotoUpload.tsx
@@ -8,7 +8,7 @@ import { useQuery } from '@tanstack/react-query';
import { categoriesService } from '../../services/categories.service';
import { settingsService } from '../../services/settings.service';
import { useTranslation } from 'react-i18next';
-import { extensionsToMimeTypes, extensionsToAcceptString } from '../../utils/fileTypes';
+import { extensionsToMimeTypes, extensionsToAcceptString, extensionsToLabel } from '../../utils/fileTypes';
import { useUploadProgress } from '../../hooks/useUploadProgress';
interface PhotoUploadProps {
@@ -118,6 +118,15 @@ export const PhotoUpload: React.FC
- {t('upload.fileRequirements', { limit: maxFilesPerUpload })} + {t('upload.fileRequirements', { formats: formatsLabel, limit: maxFilesPerUpload, sizeLimit: maxFileSizeMb })}