PHP Form URL/E-mail
Email addresses and website URLs are among the most common form inputs — and among the easiest to validate incorrectly. Hand-written regular expressions for them are usually either too strict (rejecti
Email addresses and website URLs are among the most common form inputs — and among the easiest to validate incorrectly. Hand-written regular expressions for them are usually either too strict (rejecting valid addresses) or too loose (accepting dangerous input). PHP's filter extension provides tested validators, and this lesson shows how to use them correctly, including their limitations.
Validating Email Addresses
FILTER_VALIDATE_EMAIL
<?php
$emails = [
'asha@example.com',
'Asha.K+news@Example.COM',
'asha@localhost',
'asha@@example.com',
'asha@example',
'.asha@example.com',
'asha @example.com',
];
foreach ($emails as $email) {
$result = filter_var($email, FILTER_VALIDATE_EMAIL) !== false ? 'valid' : 'invalid';
echo str_pad($email, 26) . $result . "\n";
}
Output
asha@example.com valid
Asha.K+news@Example.COM valid
asha@localhost invalid
asha@@example.com invalid
asha@example invalid
.asha@example.com invalid
asha @example.com invalid
What the Filter Checks
- There is exactly one
@separating a local part and a domain. - The local part uses allowed characters — including
.,+, and_— and does not start or end with a dot. - The domain consists of valid labels separated by dots; a domain without a dot (
localhost,example) is rejected. - No spaces are allowed.
On success, filter_var() returns the email string; on failure, false.
What the Filter Does Not Check
- Whether the domain exists or accepts mail.
- Whether the mailbox exists.
- Whether the user actually owns the address.
The only reliable way to confirm an email address is to send a verification email containing a unique link or code. Format validation simply catches typos early.
International Email Addresses
<?php
$email = 'ஆஷா@example.com';
var_dump(filter_var($email, FILTER_VALIDATE_EMAIL));
var_dump(filter_var($email, FILTER_VALIDATE_EMAIL, FILTER_FLAG_EMAIL_UNICODE));
Output
bool(false)
string(21) "ஆஷா@example.com"
By default, only ASCII characters are allowed in the local part. FILTER_FLAG_EMAIL_UNICODE (PHP 7.1+) accepts Unicode local parts. (var_dump() reports the length in bytes: each Tamil character here takes 3 bytes in UTF-8.) International domain names (such as Tamil or Hindi domains) must first be converted to their ASCII form with idn_to_ascii() from the intl extension. Before accepting Unicode addresses, check that your mail server and email provider support them.
Normalizing Email Addresses
<?php
function normalizeEmail(string $input): ?string
{
$email = trim($input);
if (filter_var($email, FILTER_VALIDATE_EMAIL) === false) {
return null;
}
$at = strrpos($email, '@');
return substr($email, 0, $at) . '@' . strtolower(substr($email, $at + 1));
}
var_dump(normalizeEmail(' Asha.K+news@Example.COM '));
var_dump(normalizeEmail('asha@example'));
Output
string(23) "Asha.K+news@example.com"
NULL
- Surrounding spaces (common when copying and pasting) are removed before validation.
- The domain is lowercased because domain names are case-insensitive.
- The local part is kept as typed: technically it may be case-sensitive, although most providers ignore case. When checking for duplicate accounts, many applications compare emails case-insensitively (for example, with a case-insensitive database collation).
Validating URLs
FILTER_VALIDATE_URL
<?php
$urls = [
'https://gocourse.in/php/arrays',
'http://localhost:8000/test',
'gocourse.in',
'https://exa mple.com',
'ftp://files.example.com/report.pdf',
];
foreach ($urls as $url) {
$result = filter_var($url, FILTER_VALIDATE_URL) !== false ? 'valid' : 'invalid';
echo str_pad($url, 36) . $result . "\n";
}
Output
https://gocourse.in/php/arrays valid
http://localhost:8000/test valid
gocourse.in invalid
https://exa mple.com invalid
ftp://files.example.com/report.pdf valid
- A URL must have a scheme (
https://);gocourse.inalone is rejected, even though users often type it this way. - Spaces and other invalid characters are rejected.
- Any scheme is accepted — not only
httpandhttps.
Optional Flags
| Flag | Requires |
|---|---|
FILTER_FLAG_PATH_REQUIRED | A path after the host (https://example.com/page) |
FILTER_FLAG_QUERY_REQUIRED | A query string (?key=value) |
The Scheme Problem: Dangerous URLs
Because FILTER_VALIDATE_URL accepts any scheme, it also accepts URLs that are dangerous when used as links, such as javascript: URLs crafted to include a host (for example, javascript://example.com/%0Aalert(1)). If such a "website" from a user profile is printed into <a href="...">, clicking it runs JavaScript in your site's context — an XSS attack.
Always restrict the scheme for URLs that will become links:
<?php
function validateWebUrl(string $input): ?string
{
$url = trim($input);
if (filter_var($url, FILTER_VALIDATE_URL) === false) {
return null;
}
$scheme = strtolower((string) parse_url($url, PHP_URL_SCHEME));
return in_array($scheme, ['http', 'https'], true) ? $url : null;
}
$tests = [
' https://gocourse.in/php ',
'gocourse.in',
'javascript://example.com/%0Aalert(1)',
'ftp://files.example.com/report.pdf',
];
foreach ($tests as $input) {
$result = validateWebUrl($input);
echo str_pad(trim($input), 38) . ($result === null ? 'rejected' : 'accepted') . "\n";
}
Output
https://gocourse.in/php accepted
gocourse.in rejected
javascript://example.com/%0Aalert(1) rejected
ftp://files.example.com/report.pdf rejected
How It Works
- The input is trimmed, then validated as a URL.
parse_url(..., PHP_URL_SCHEME)extracts the scheme, which is compared with an allow-list.- Only
httpandhttpsURLs are accepted, which blocksjavascript:,data:,file:, and other schemes.
Being Helpful With Missing Schemes
Users often type gocourse.in without https://. A friendly form can add the scheme before validating:
<?php
function withDefaultScheme(string $input): string
{
$url = trim($input);
return preg_match('#^[a-z][a-z0-9+.-]*://#i', $url) === 1 ? $url : 'https://' . $url;
}
echo withDefaultScheme('gocourse.in/php') . "\n";
echo withDefaultScheme('http://example.com') . "\n";
Output
https://gocourse.in/php
http://example.com
The result must still pass validateWebUrl() afterwards.
Displaying User-Supplied Links Safely
Even after validation, escape URLs when printing them, and mark external links appropriately:
<?php
$website = 'https://example.com/profile?name=asha&ref=gocourse';
?>
<a href="<?= htmlspecialchars($website) ?>" rel="nofollow ugc noopener" target="_blank">
<?= htmlspecialchars(parse_url($website, PHP_URL_HOST)) ?>
</a>
Output (HTML)
<a href="https://example.com/profile?name=asha&ref=gocourse" rel="nofollow ugc noopener" target="_blank">
example.com
</a>
htmlspecialchars()escapes&as&(correct in HTML attributes) and neutralizes quotes that could break out of the attribute.rel="nofollow ugc"tells search engines that the link is user-generated and not endorsed.noopenerprevents the opened page from controlling your page throughwindow.opener.
Security note: If your server ever fetches a URL supplied by a user (for previews or imports), validating the format is not enough. An attacker could point it at internal addresses (
http://127.0.0.1/admin, cloud metadata services). This is called Server-Side Request Forgery (SSRF); such features need additional checks on the resolved IP address.
Common Mistakes
Writing Your Own Email Regex
Effect: Valid addresses (with + or long domains) are rejected, or invalid ones accepted.
Fix: Use FILTER_VALIDATE_EMAIL, then verify ownership by email.
Validating Before Trimming
Effect: Pasted values with spaces are rejected.
Fix: trim() first, then validate.
Accepting Any URL Scheme for Links
Effect: javascript: links lead to XSS.
Fix: Allow only http and https.
Printing URLs Without Escaping
Effect: Quotes in a URL can break out of the href attribute.
Fix: htmlspecialchars() in attributes.
Treating a Valid Format as Proof
Effect: Fake or mistyped (but well-formed) addresses are trusted.
Fix: Send verification emails; check link targets if they matter.
Best Practices
- Trim input, then validate with
FILTER_VALIDATE_EMAILorFILTER_VALIDATE_URL. - Normalize email domains to lowercase and verify addresses with a confirmation email.
- Restrict URL schemes to
httpandhttpsfor user-supplied links. - Escape URLs on output and add
relattributes for external, user-generated links. - Treat server-side fetching of user URLs as a separate security problem (SSRF).
Official reference: https://www.php.net/manual/en/filter.filters.validate.php