建安五年(200年)正月,袁绍发布讨曹檄文,二月进军黎阳,企图渡河寻求与曹军主力决战。他首先派颜良进攻白马的东郡太守刘延,企图夺取黄河南岸要点,以保障主力渡河。
四月,曹操为争取主动,求得初战的胜利,亲自率兵北上解救白马之围。此时谋士荀攸认为袁绍兵多,建议声东击西,分散其兵力,先引兵至延津,伪装渡河攻袁后方,使袁绍分兵向西,然后遣轻骑迅速袭击进攻白马的袁军,攻其不备,定可击败颜良。曹操采纳了这一建议,袁绍果然分兵延津。
曹操乃乘机率轻骑,派张辽、关羽为前锋,急趋白马。关羽迅速迫近颜良军,颜良仓促应战被斩杀,袁军溃败。曹操解了白马之围后,迁徙白马的百姓沿黄河向西撤退,袁绍率军渡河追击,军至延津南,派大将文丑与刘备继续率兵追击曹军。曹操当时只有骑兵600,驻于南阪(在白马南)下,而袁军达五六千骑,尚有步兵在后跟进。曹操令士卒解鞍放马,并故意将辎重丢弃道旁。袁军一见果然中计,纷纷争抢财物。
曹操突然发起攻击,终于击败袁军,杀了文丑,顺利退回官渡。
袁军初战失利,但兵力仍占优势。七月,进军阳武(今河南中牟北),准备南下进攻许昌。八月,袁军主力接近官渡,依沙堆立营,东西宽约数十里。曹操也立营与袁军对峙。九月,曹军一度出击,没有获胜,退回营垒坚守。袁绍构筑楼橹,堆土如山,用箭俯射曹营。曹军制作了一种抛石用的霹雳车,发石击毁了袁军所筑的楼橹。袁军又掘地道进攻,曹军也在营内掘长堑相抵抗。双方相持3个月,曹操外境困难,前方兵少粮缺,士卒疲乏,后方也不稳固,曹操几乎失去坚守的信心。
荀彧力主坚持,曹操于是一方面决心坚持危局,加强防守,命负责后勤补给的任峻采取10路纵队为一部,缩短运输队的前后距离,并用复阵(两列阵),加强护卫,防止袁军袭击;另一方面积极寻求和捕捉战机,击败袁军,不久派徐晃、史涣截击、烧毁袁军数千辆粮车,增加了袁军的困难。
同年十月,袁绍又派车运粮,并令淳于琼率兵万人护送,屯积在袁军大营以北约20公里的故市(河南延津县内)、乌巢(今河南延津东南)。恰在这时,袁绍谋士许攸投降曹操,建议曹操轻兵奇袭乌巢,烧其辎重。
曹操立即付诸实行,留曹洪、荀攸守营垒,亲自率领步骑5000,冒用袁军旗号,衔枚缚马口,每人带一束柴草,利用夜暗走小路偷袭乌巢。到达后立即围攻放火。袁绍听说曹操袭击乌巢,又作出错误处置,只派一部兵力救援乌巢,用主力猛攻官渡曹军营垒。哪知曹营坚固,攻打不下。
当曹军急攻乌巢淳于琼营时,袁绍增援的部队已经迫近。曹操励士死战,大破袁军,杀淳于琼,并烧毁其全部粮草。乌巢粮草被烧的消息传到袁军前线,袁军军心动摇,内部分裂。曹军乘势出击,大败袁军。袁绍仓惶带800骑退回河北,曹军先后歼灭和坑杀袁军7万余人,官渡之战就这样以曹胜袁败而告结束。