Quick answer: Match a newline in Python regex with the explicit pattern \n or \r?\n, and use re.MULTILINE when ^ and $ should match line boundaries. Use re.DOTALL when dot should span newline characters.

Regex new line matching in Python depends on what you want to match. Use \n when you need the actual line break character, use re.MULTILINE when ^ and $ should work per line, and use re.DOTALL when a dot should match across line breaks.
These choices are separate. re.MULTILINE changes how anchors behave; it does not make . match newline characters. re.DOTALL changes dot matching; it does not make ^ and $ operate on each line. Mixing those ideas is the most common source of confusing regex results.
Start by deciding whether the newline itself is part of the pattern. If it is, match \n directly or normalize the text first. If you only care about line starts and ends, use anchors with multiline mode. If you need to capture a block of text across several lines, use dot-all mode or a more specific character class.
Newline bugs are easier to solve when you test against a small sample. Print the sample with repr(), confirm whether it contains \n or \r\n, and then choose the smallest regex mode that fits the job. Avoid enabling every flag by habit.
The official Python re documentation, regular expression HOWTO, re.MULTILINE documentation, re.DOTALL documentation, and str.splitlines documentation are the primary references.
Match A Literal Newline
Use \n in the pattern when the line break itself must appear between two pieces of text.
import re
text = "first line\nsecond line"
match = re.search(r"first line\nsecond line", text)
print(match is not None)
This pattern only matches when the newline is present at that position. If the text uses Windows-style line endings, normalize them before applying the regex.
Use MULTILINE For Line Anchors
Without re.MULTILINE, ^ and $ work at the start and end of the whole string. With multiline mode, they also match the start and end of each line.
import re
text = "alpha\nbeta\ngamma"
matches = re.findall(r"^b\w+", text, flags=re.MULTILINE)
print(matches)
This finds beta because the anchor can match after a newline. Without the flag, the same pattern would only check the start of the full string.

Use DOTALL For Text Across Lines
By default, the dot does not match newline characters. Add re.DOTALL when a single pattern needs to span multiple lines.
import re
text = "<section>\ncontent\n</section>"
match = re.search(r"<section>.*</section>", text, flags=re.DOTALL)
print(match.group(0))
Dot-all mode is useful for controlled text formats, but be careful with broad .* patterns. Prefer a tighter pattern when the input can contain several similar blocks.
If a pattern suddenly captures too much text after adding re.DOTALL, make the repetition lazy with .*? or replace the dot with a character class that stops at the boundary you expect. The goal is to cross line breaks only where the format requires it.
Find Blank Lines
Blank-line detection is a good use case for anchors and multiline mode. The following pattern finds empty lines or lines containing only spaces and tabs.
import re
text = "one\n\n \ntwo"
blank_lines = re.findall(r"^[ \t]*$", text, flags=re.MULTILINE)
print(len(blank_lines))
This checks each line independently. It is often cleaner than trying to count newline characters manually.

Normalize Newline Styles First
Text from different systems may contain \n, \r\n, or \r. Normalize line endings before matching if the input source is mixed.
import re
raw_text = "alpha\r\nbeta\rgamma\n"
text = re.sub(r"\r\n?|\n", "\n", raw_text)
print(text.split("\n"))
After normalization, the rest of your regex can assume a single newline style. That keeps patterns shorter and easier to review.
Use splitlines For Line-Oriented Work
Regex is not always the best tool for line handling. If you need to process one line at a time, splitlines() is often clearer.
import re
text = "error: one\ninfo: two\nerror: three"
errors = [
line
for line in text.splitlines()
if re.search(r"^error:", line)
]
print(errors)
This approach keeps each regex small because it runs against one line at a time. It also avoids accidental matches that cross line boundaries.

Fix Checklist
Use \n when the line break is part of the match. Use re.MULTILINE when anchors should work for each line. Use re.DOTALL when the dot should match across line breaks.
Normalize mixed line endings before matching. For files, read the text with a known encoding, inspect a small sample, and decide whether regex is better than line-by-line processing.
When a pattern fails, print repr(text) for a small input slice. Seeing the actual \n and \r\n characters usually makes the correct regex choice obvious.
Keep regex tests close to the text shape you process in production. A pattern that works on a one-line example can fail on copied logs, generated reports, or files from another operating system when line endings differ.
Separate Newline Matching From Line Anchors
A newline is data in the input string. Matching the character and changing how anchors work are separate requirements. Use \n for a line-feed newline, \r?\n for common text that may contain Windows line endings, and re.MULTILINE when ^ and $ should operate at each line boundary.
import re
text = "first line\nsecond line"
lines = re.findall(r"^.+$", text, flags=re.MULTILINE)
print(lines)

Use DOTALL Only For Cross-Line Text
By default, the dot does not match a newline. Pass re.DOTALL, or use the inline flag (?s), when a wildcard should cross line breaks. Prefer a narrower character class or an explicit delimiter when the format has a clear boundary; broad wildcards can consume more than intended.
Normalize Newlines At The Boundary
If a file or API can use several newline conventions, normalize the text once at input and then write patterns for the normalized representation. Test empty lines, a final newline, mixed line endings, and Unicode text. A regex that works on one sample line is not automatically a complete line parser.
For broader text patterns, compare newline matching with optional groups and substrings. Read regex optional group and python substring for the related workflow.
Frequently Asked Questions
How do I match a new line in Python regex?
Use \n for a line-feed newline or \r?\n when the input may use Windows line endings.
What does re.MULTILINE do?
It makes ^ and $ match at the start and end of each line as well as at the boundaries of the whole string.
How do I make a regex dot match newlines?
Use re.DOTALL, or the inline flag (?s), when the dot should match newline characters across line breaks.
Should I normalize line endings before matching?
Often yes. Normalize newline conventions at the input boundary, then test empty lines, final newlines, and mixed line endings explicitly.