Matching File Extensions with Regex
File extensions are the little suffixes that tell your operating system, browser, and code what type of content is inside a file — .jpg, .pdf, .mp4, .tar.gz. They're used everywhere: filtering directory listings, validating upload forms, routing files through processing pipelines, generating content-type headers. Regex is the fastest way to match them, but there are three real traps — case sensitivity, the missing $ anchor, and confusing extension with file type. Below are the patterns that get it right.
The Core Patterns
Extract any file extension
/\.([a-zA-Z0-9]+)$/
// Captures group 1 = the extension without the dot
// photo.jpg → 'jpg'
// document.PDF → 'PDF'
// archive.tar.gz → 'gz' (only the LAST extension)
// README → no match (no extension)The capture group is everything after the final dot. Note that this returns just the last extension — for compound extensions like .tar.gz you need a different approach.
Match a specific extension (case-insensitive)
/\.jpg$/i
// Matches: photo.jpg IMAGE.JPG holiday.Jpg
// Rejects: photo.jpeg malicious.jpg.exe photo.jpg.txt
// The $ anchor is critical to prevent .jpg.exe attacks.Image files
/\.(jpg|jpeg|png|gif|webp|svg|bmp|tiff?|avif|heic|heif)$/i
// Modern image formats including AVIF (2020+) and HEIC (Apple).
// tiff? matches both .tif and .tiff.
// For web-safe only (no SVG XSS risk):
/\.(jpg|jpeg|png|gif|webp)$/iVideo files
/\.(mp4|mov|avi|mkv|webm|flv|wmv|m4v|3gp|ogv)$/i
// Covers all mainstream video containers.Audio files
/\.(mp3|wav|flac|aac|ogg|m4a|opus|wma|aiff?)$/i
// aiff? matches both .aif and .aiff.Documents
/\.(pdf|docx?|xlsx?|pptx?|txt|rtf|odt|ods|odp|csv|md)$/i
// docx? matches .doc and .docx. Same for .xlsx and .pptx.Code files
/\.(js|jsx|ts|tsx|py|rb|go|rs|java|kt|swift|c|cpp|cc|h|hpp|cs|php|scala|sh|bash|zsh)$/i
// Add whatever languages your app supports.Compound extensions (.tar.gz, .tar.bz2)
/\.(tar\.(gz|bz2|xz)|tgz|tbz2|txz|zip|7z|rar|gz|bz2|xz|Z)$/i
// Handles both compound (.tar.gz) and single-part (.zip) archives.
// tgz/tbz2/txz are shortened forms of tar.gz/tar.bz2/tar.xz.Language-Specific Usage
JavaScript
const IMAGE = /\.(jpg|jpeg|png|gif|webp|svg)$/i;
const DOC = /\.(pdf|docx?|xlsx?|txt)$/i;
function isImage(filename) { return IMAGE.test(filename); }
function getExtension(filename) {
const m = filename.match(/\.([^.]+)$/);
return m ? m[1].toLowerCase() : null;
}
isImage('photo.JPG'); // true
isImage('malicious.jpg.exe'); // false (anchored with $)
getExtension('archive.tar.gz'); // 'gz'
getExtension('README'); // null
// Filter a list of filenames
const files = ['photo.jpg', 'doc.pdf', 'script.js', 'image.png'];
const images = files.filter(f => IMAGE.test(f));
// ['photo.jpg', 'image.png']Python
import re
import os
IMAGE = re.compile(r'\.(jpg|jpeg|png|gif|webp|svg)$', re.I)
def is_image(filename: str) -> bool:
return bool(IMAGE.search(filename))
def get_extension(filename: str) -> str:
# Regex approach
m = re.search(r'\.([^.]+)$', filename)
return m.group(1).lower() if m else ''
# Python also has a stdlib helper:
name, ext = os.path.splitext('photo.jpg') # ('photo', '.jpg')
# splitext handles edge cases like leading dot filenames ('.gitignore') correctly.
is_image('photo.JPG') # True
get_extension('archive.tar.gz') # 'gz'PHP
function isImage(string $filename): bool {
return (bool) preg_match(
'/\.(jpg|jpeg|png|gif|webp|svg)$/i',
$filename
);
}
function getExtension(string $filename): string {
// PHP has a builtin — usually preferable
return strtolower(pathinfo($filename, PATHINFO_EXTENSION));
}
isImage('photo.JPG'); // true
getExtension('archive.tar.gz'); // 'gz'Common Pitfalls
Missing $ anchor = security vulnerability
/\\.jpg/i matches malicious.jpg.exe because .jpg appears in the middle of the filename. Attackers exploit this in upload forms: rename shell.php to image.jpg.php, pass the client-side check, and get code execution server-side. Always end your extension regex with $.
Extension is not file type
Anyone can rename malicious.exe to photo.jpg. The extension is just a naming convention. For security, always check the actual file magic bytes server-side: JavaScript can read the first bytes via the File API; Python uses python-magic; PHP uses finfo_file(). Regex on the filename is UX (helpful error messages), not security.
Compound extensions require special handling
The last-segment approach returns gz for archive.tar.gz— misleading because you actually want to know it's a tarball. Either match compound patterns explicitly, or use a language-specific helper (Python pathlib.Path().suffixes returns ['.tar', '.gz']).
Leading-dot filenames are edge cases
On Unix, .gitignore, .env, .bashrc are files with a leading dot but no traditional extension. /\\.([^.]+)$/ matches these — returning gitignore as the "extension". Handle explicitly if this matters: if (filename.startsWith('.') && filename.indexOf('.', 1) === -1) noExt();
Case sensitivity trips up validation
photo.JPG, photo.jpg, and photo.Jpg all refer to the same file type. Always use the /i flag or lowercase the filename before matching. Silently rejecting uppercase extensions is a common UX bug.
File Extension Regex Cheatsheet
| Category | Pattern | Common |
|---|---|---|
| Any extension | /\.([a-zA-Z0-9]+)$/ | extract type |
| Images (safe) | /\.(jpg|jpeg|png|gif|webp)$/i | upload forms |
| Videos | /\.(mp4|mov|avi|mkv|webm)$/i | media |
| Documents | /\.(pdf|docx?|xlsx?|txt)$/i | office |
| Archives | /\.(zip|tar\.gz|tgz|7z|rar)$/i | downloads |
Testing Your File Extension Regex
Use the live Regex Tester above with these test strings:
- Match:
photo.jpg,document.pdf,video.MP4 - Match (compound):
backup.tar.gz,archive.zip - Match (case):
IMAGE.JPG,Photo.Jpg - Reject (no anchor test):
malicious.jpg.exe(must reject if using strict extension regex) - Edge:
README(no extension),.gitignore(leading dot only)
Common Mistakes When Writing Extension Regex
- Forgetting the $ anchor. Enables
.jpg.exeattacks. Always end with$for validation regex. - Escaping wrong.
.matches any character.\\.matches a literal dot. Use\\.in your character class for the file extension separator. - Case-sensitive validation.
.JPGfails a strict regex. Use/ior lowercase the input. - Trusting extension for security. Extensions are trivially forgeable. Check magic bytes for anything sensitive.
- Ignoring path separators.
/uploads/photo.jpgpasses an extension regex fine, but the same regex should not include forward slashes in the "name" part. If you're matching path segments, split on/first.
Performance Notes
File extension regexes are among the fastest patterns — bounded character classes, no alternation ambiguity, no catastrophic backtracking possible. You can filter tens of thousands of filenames per second. If you're processing millions of paths in a build tool, precompile the regex once (module-level constant) and reuse. In JavaScript, using filename.endsWith('.jpg') is even faster than a regex — but you lose case insensitivity and multi-extension matching, so regex is worth the microseconds for real applications.